summaryrefslogtreecommitdiff
path: root/lib/mlibc/options/internal
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2024-03-07 17:28:00 -0500
committerIan Moffett <ian@osmora.org>2024-03-07 17:28:32 -0500
commitbd5969fc876a10b18613302db7087ef3c40f18e1 (patch)
tree7c2b8619afe902abf99570df2873fbdf40a4d1a1 /lib/mlibc/options/internal
parenta95b38b1b92b172e6cc4e8e56a88a30cc65907b0 (diff)
lib: Add mlibc
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'lib/mlibc/options/internal')
-rw-r--r--lib/mlibc/options/internal/aarch64-include/mlibc/arch-defs.hpp12
-rw-r--r--lib/mlibc/options/internal/aarch64-include/mlibc/thread.hpp21
-rw-r--r--lib/mlibc/options/internal/aarch64/fenv.S69
-rw-r--r--lib/mlibc/options/internal/aarch64/mlibc_crtbegin.S29
-rw-r--r--lib/mlibc/options/internal/aarch64/mlibc_crtend.S22
-rw-r--r--lib/mlibc/options/internal/aarch64/setjmp.S63
-rw-r--r--lib/mlibc/options/internal/gcc-extra/cxxabi.cpp20
-rw-r--r--lib/mlibc/options/internal/gcc/guard-abi.cpp68
-rw-r--r--lib/mlibc/options/internal/gcc/initfini.cpp23
-rw-r--r--lib/mlibc/options/internal/gcc/stack_protector.cpp31
-rw-r--r--lib/mlibc/options/internal/generic/allocator.cpp196
-rw-r--r--lib/mlibc/options/internal/generic/charcode.cpp244
-rw-r--r--lib/mlibc/options/internal/generic/charset.cpp144
-rw-r--r--lib/mlibc/options/internal/generic/debug.cpp22
-rw-r--r--lib/mlibc/options/internal/generic/ensure.cpp18
-rw-r--r--lib/mlibc/options/internal/generic/essential.cpp217
-rw-r--r--lib/mlibc/options/internal/generic/frigg.cpp14
-rw-r--r--lib/mlibc/options/internal/generic/global-config.cpp27
-rw-r--r--lib/mlibc/options/internal/generic/inline-emitter.cpp16
-rw-r--r--lib/mlibc/options/internal/generic/locale.cpp87
-rw-r--r--lib/mlibc/options/internal/generic/sigset.cpp37
-rw-r--r--lib/mlibc/options/internal/generic/strings.cpp22
-rw-r--r--lib/mlibc/options/internal/generic/threads.cpp342
-rw-r--r--lib/mlibc/options/internal/generic/ubsan.cpp254
-rw-r--r--lib/mlibc/options/internal/include/bits/cpu_set.h13
-rw-r--r--lib/mlibc/options/internal/include/bits/ensure.h45
-rw-r--r--lib/mlibc/options/internal/include/bits/ether_addr.h10
-rw-r--r--lib/mlibc/options/internal/include/bits/inline-definition.h19
-rw-r--r--lib/mlibc/options/internal/include/bits/machine.h86
-rw-r--r--lib/mlibc/options/internal/include/bits/mbstate.h12
-rw-r--r--lib/mlibc/options/internal/include/bits/nl_item.h82
-rw-r--r--lib/mlibc/options/internal/include/bits/null.h16
-rw-r--r--lib/mlibc/options/internal/include/bits/off_t.h8
-rw-r--r--lib/mlibc/options/internal/include/bits/sigset_t.h25
-rw-r--r--lib/mlibc/options/internal/include/bits/size_t.h6
-rw-r--r--lib/mlibc/options/internal/include/bits/ssize_t.h15
-rw-r--r--lib/mlibc/options/internal/include/bits/threads.h79
-rw-r--r--lib/mlibc/options/internal/include/bits/types.h319
-rw-r--r--lib/mlibc/options/internal/include/bits/wchar.h9
-rw-r--r--lib/mlibc/options/internal/include/bits/wchar_t.h12
-rw-r--r--lib/mlibc/options/internal/include/bits/winsize.h13
-rw-r--r--lib/mlibc/options/internal/include/bits/wint_t.h6
-rw-r--r--lib/mlibc/options/internal/include/mlibc/all-sysdeps.hpp33
-rw-r--r--lib/mlibc/options/internal/include/mlibc/allocator.hpp37
-rw-r--r--lib/mlibc/options/internal/include/mlibc/bitutil.hpp34
-rw-r--r--lib/mlibc/options/internal/include/mlibc/charcode.hpp124
-rw-r--r--lib/mlibc/options/internal/include/mlibc/charset.hpp40
-rw-r--r--lib/mlibc/options/internal/include/mlibc/debug.hpp27
-rw-r--r--lib/mlibc/options/internal/include/mlibc/file-window.hpp64
-rw-r--r--lib/mlibc/options/internal/include/mlibc/fsfd_target.hpp15
-rw-r--r--lib/mlibc/options/internal/include/mlibc/global-config.hpp19
-rw-r--r--lib/mlibc/options/internal/include/mlibc/internal-sysdeps.hpp41
-rw-r--r--lib/mlibc/options/internal/include/mlibc/locale.hpp12
-rw-r--r--lib/mlibc/options/internal/include/mlibc/lock.hpp124
-rw-r--r--lib/mlibc/options/internal/include/mlibc/stack_protector.hpp10
-rw-r--r--lib/mlibc/options/internal/include/mlibc/strings.hpp12
-rw-r--r--lib/mlibc/options/internal/include/mlibc/strtofp.hpp165
-rw-r--r--lib/mlibc/options/internal/include/mlibc/strtol.hpp159
-rw-r--r--lib/mlibc/options/internal/include/mlibc/tcb.hpp180
-rw-r--r--lib/mlibc/options/internal/include/mlibc/threads.hpp27
-rw-r--r--lib/mlibc/options/internal/include/mlibc/tid.hpp18
-rw-r--r--lib/mlibc/options/internal/include/stdint.h150
-rw-r--r--lib/mlibc/options/internal/riscv64-include/mlibc/arch-defs.hpp12
-rw-r--r--lib/mlibc/options/internal/riscv64-include/mlibc/thread.hpp23
-rw-r--r--lib/mlibc/options/internal/riscv64/fenv.S57
-rw-r--r--lib/mlibc/options/internal/riscv64/mlibc_crtbegin.S29
-rw-r--r--lib/mlibc/options/internal/riscv64/mlibc_crtend.S21
-rw-r--r--lib/mlibc/options/internal/riscv64/setjmp.S71
-rwxr-xr-xlib/mlibc/options/internal/x86-include/mlibc/arch-defs.hpp13
-rwxr-xr-xlib/mlibc/options/internal/x86-include/mlibc/thread.hpp21
-rw-r--r--lib/mlibc/options/internal/x86/fenv.S168
-rw-r--r--lib/mlibc/options/internal/x86/mlibc_crtbegin.S29
-rw-r--r--lib/mlibc/options/internal/x86/mlibc_crtend.S24
-rw-r--r--lib/mlibc/options/internal/x86/setjmp.S53
-rw-r--r--lib/mlibc/options/internal/x86_64-include/mlibc/arch-defs.hpp12
-rw-r--r--lib/mlibc/options/internal/x86_64-include/mlibc/thread.hpp20
-rw-r--r--lib/mlibc/options/internal/x86_64/fenv.S102
-rw-r--r--lib/mlibc/options/internal/x86_64/mlibc_crtbegin.S29
-rw-r--r--lib/mlibc/options/internal/x86_64/mlibc_crtend.S24
-rw-r--r--lib/mlibc/options/internal/x86_64/setjmp.S54
80 files changed, 4826 insertions, 0 deletions
diff --git a/lib/mlibc/options/internal/aarch64-include/mlibc/arch-defs.hpp b/lib/mlibc/options/internal/aarch64-include/mlibc/arch-defs.hpp
new file mode 100644
index 0000000..0a4789f
--- /dev/null
+++ b/lib/mlibc/options/internal/aarch64-include/mlibc/arch-defs.hpp
@@ -0,0 +1,12 @@
+#ifndef MLIBC_ARCH_DEFS_HPP
+#define MLIBC_ARCH_DEFS_HPP
+
+#include <stddef.h>
+
+namespace mlibc {
+
+inline constexpr size_t page_size = 0x1000;
+
+} // namespace mlibc
+
+#endif // MLIBC_ARCH_DEFS_HPP
diff --git a/lib/mlibc/options/internal/aarch64-include/mlibc/thread.hpp b/lib/mlibc/options/internal/aarch64-include/mlibc/thread.hpp
new file mode 100644
index 0000000..b62d832
--- /dev/null
+++ b/lib/mlibc/options/internal/aarch64-include/mlibc/thread.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <stdint.h>
+#include <mlibc/tcb.hpp>
+
+namespace mlibc {
+
+inline Tcb *get_current_tcb() {
+ // On AArch64, TPIDR_EL0 points to 0x10 bytes before the first TLS block.
+ uintptr_t ptr;
+ asm ("mrs %0, tpidr_el0" : "=r"(ptr));
+ return reinterpret_cast<Tcb *>(ptr + 0x10 - sizeof(Tcb));
+}
+
+inline uintptr_t get_sp() {
+ uintptr_t sp;
+ asm ("mov %0, sp" : "=r"(sp));
+ return sp;
+}
+
+} // namespace mlibc
diff --git a/lib/mlibc/options/internal/aarch64/fenv.S b/lib/mlibc/options/internal/aarch64/fenv.S
new file mode 100644
index 0000000..1b02e87
--- /dev/null
+++ b/lib/mlibc/options/internal/aarch64/fenv.S
@@ -0,0 +1,69 @@
+# The functions below are taken from musl.
+.global fegetround
+.type fegetround,%function
+fegetround:
+ mrs x0, fpcr
+ and w0, w0, #0xc00000
+ ret
+
+.global __fesetround
+.hidden __fesetround
+.type __fesetround,%function
+__fesetround:
+ mrs x1, fpcr
+ bic w1, w1, #0xc00000
+ orr w1, w1, w0
+ msr fpcr, x1
+ mov w0, #0
+ ret
+
+.global fetestexcept
+.type fetestexcept,%function
+fetestexcept:
+ and w0, w0, #0x1f
+ mrs x1, fpsr
+ and w0, w0, w1
+ ret
+
+.global feclearexcept
+.type feclearexcept,%function
+feclearexcept:
+ and w0, w0, #0x1f
+ mrs x1, fpsr
+ bic w1, w1, w0
+ msr fpsr, x1
+ mov w0, #0
+ ret
+
+.global feraiseexcept
+.type feraiseexcept,%function
+feraiseexcept:
+ and w0, w0, #0x1f
+ mrs x1, fpsr
+ orr w1, w1, w0
+ msr fpsr, x1
+ mov w0, #0
+ ret
+
+.global fegetenv
+.type fegetenv,%function
+fegetenv:
+ mrs x1, fpcr
+ mrs x2, fpsr
+ stp w1, w2, [x0]
+ mov w0, #0
+ ret
+
+// TODO preserve some bits
+.global fesetenv
+.type fesetenv,%function
+fesetenv:
+ mov x1, #0
+ mov x2, #0
+ cmn x0, #1
+ b.eq 1f
+ ldp w1, w2, [x0]
+1: msr fpcr, x1
+ msr fpsr, x2
+ mov w0, #0
+ ret
diff --git a/lib/mlibc/options/internal/aarch64/mlibc_crtbegin.S b/lib/mlibc/options/internal/aarch64/mlibc_crtbegin.S
new file mode 100644
index 0000000..b99748b
--- /dev/null
+++ b/lib/mlibc/options/internal/aarch64/mlibc_crtbegin.S
@@ -0,0 +1,29 @@
+
+.section .data
+.hidden __dso_handle
+.global __dso_handle
+__dso_handle:
+ .quad __dso_handle
+
+.section .init
+.hidden _init
+.global _init
+_init:
+
+.section .fini
+.hidden _fini
+.global _fini
+_fini:
+
+.section .ctors
+.hidden __CTOR_LIST__
+.global __CTOR_LIST__
+__CTOR_LIST__:
+
+.section .dtors
+.hidden __DTOR_LIST__
+.global __DTOR_LIST__
+__DTOR_LIST__:
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/options/internal/aarch64/mlibc_crtend.S b/lib/mlibc/options/internal/aarch64/mlibc_crtend.S
new file mode 100644
index 0000000..2bf6254
--- /dev/null
+++ b/lib/mlibc/options/internal/aarch64/mlibc_crtend.S
@@ -0,0 +1,22 @@
+
+.hidden __mlibc_do_ctors
+.hidden __mlibc_do_dtors
+
+.section .init
+ b __mlibc_do_ctors
+
+.section .fini
+ b __mlibc_do_dtors
+
+.section .ctors
+.hidden __CTOR_END__
+.global __CTOR_END__
+__CTOR_END__:
+
+.section .dtors
+.hidden __DTOR_END__
+.global __DTOR_END__
+__DTOR_END__:
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/options/internal/aarch64/setjmp.S b/lib/mlibc/options/internal/aarch64/setjmp.S
new file mode 100644
index 0000000..9dddaa8
--- /dev/null
+++ b/lib/mlibc/options/internal/aarch64/setjmp.S
@@ -0,0 +1,63 @@
+// vim: ft=arm64asm
+
+.extern __sigsetjmp
+
+.type __setjmp, "function"
+__setjmp:
+ stp x19, x20, [x0, #0]
+ stp x21, x22, [x0, #16]
+ stp x23, x24, [x0, #24]
+ stp x25, x26, [x0, #32]
+ stp x27, x28, [x0, #48]
+ stp x29, x30, [x0, #64]
+ stp x29, x30, [x0, #80]
+ mov x4, sp
+ str x4, [x0, #96]
+
+ stp d8, d9, [x0, #112]
+ stp d10, d11, [x0, #128]
+ stp d12, d13, [x0, #144]
+ stp d14, d15, [x0, #160]
+
+ cbnz x2, 1f
+
+ mov x0, xzr
+ ret
+1:
+ b __sigsetjmp
+
+.global setjmp
+.type setjmp, "function"
+setjmp:
+ mov x2, xzr
+ b __setjmp
+
+.global sigsetjmp
+.type sigsetjmp, "function"
+sigsetjmp:
+ mov x2, #1
+ b __setjmp
+
+.global longjmp
+.type longjmp, "function"
+longjmp:
+ ldp x19, x20, [x0, #0]
+ ldp x21, x22, [x0, #16]
+ ldp x23, x24, [x0, #24]
+ ldp x25, x26, [x0, #32]
+ ldp x27, x28, [x0, #48]
+ ldp x29, x30, [x0, #64]
+ ldp x29, x30, [x0, #80]
+ ldr x4, [x0, #96]
+ mov sp, x4
+
+ ldp d8, d9, [x0, #112]
+ ldp d10, d11, [x0, #128]
+ ldp d12, d13, [x0, #144]
+ ldp d14, d15, [x0, #160]
+
+ cmp w1, 0
+ csinc w0, w1, wzr, ne
+ br x30
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/options/internal/gcc-extra/cxxabi.cpp b/lib/mlibc/options/internal/gcc-extra/cxxabi.cpp
new file mode 100644
index 0000000..c5bd92f
--- /dev/null
+++ b/lib/mlibc/options/internal/gcc-extra/cxxabi.cpp
@@ -0,0 +1,20 @@
+
+#include <bits/ensure.h>
+
+// The cxxabi needs operator delete for *deleting* destructors, i.e., destructors that
+// are called by delete expressions. We never use such expressions in mlibc.
+// Note that G++ complains if we make the operator hidden,
+// thus we use it's mangled name as a workaround.
+#if defined(__clang__)
+ extern "C" [[gnu::visibility("hidden")]] void _ZdlPv() { // operator delete (void *, size_t)
+ __ensure(!"operator delete called! delete expressions cannot be used in mlibc.");
+ }
+#else
+ extern "C" [[gnu::visibility("hidden")]] void _ZdlPvj() { // operator delete (void *, unsigned int)
+ __ensure(!"operator delete called! delete expressions cannot be used in mlibc.");
+ }
+
+ extern "C" [[gnu::visibility("hidden")]] void _ZdlPvm() { // operator delete (void *, size_t)
+ __ensure(!"operator delete called! delete expressions cannot be used in mlibc.");
+ }
+#endif \ No newline at end of file
diff --git a/lib/mlibc/options/internal/gcc/guard-abi.cpp b/lib/mlibc/options/internal/gcc/guard-abi.cpp
new file mode 100644
index 0000000..945582a
--- /dev/null
+++ b/lib/mlibc/options/internal/gcc/guard-abi.cpp
@@ -0,0 +1,68 @@
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <mlibc/debug.hpp>
+#include <mlibc/internal-sysdeps.hpp>
+
+namespace {
+
+// Itanium ABI static initialization guard.
+struct Guard {
+ // bit of the mutex member variable.
+ // indicates that the mutex is locked.
+ static constexpr int32_t locked = 1;
+
+ void lock() {
+ uint32_t v = 0;
+ if(__atomic_compare_exchange_n(&mutex, &v, Guard::locked, false,
+ __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
+ return;
+
+ mlibc::sys_libc_log("__cxa_guard_acquire contention");
+ __builtin_trap();
+ }
+
+ void unlock() {
+ __atomic_store_n(&mutex, 0, __ATOMIC_RELEASE);
+ }
+
+ // the first byte's meaning is fixed by the ABI.
+ // it indicates whether initialization has already been completed.
+ uint8_t complete;
+
+ // we use some of the remaining bytes to implement a mutex.
+ uint32_t mutex;
+};
+
+static_assert(sizeof(Guard) == sizeof(int64_t));
+
+} // namespace { }
+
+extern "C" [[ gnu::visibility("hidden") ]] void __cxa_pure_virtual() {
+ mlibc::panicLogger() << "mlibc: Pure virtual function called from IP "
+ << (void *)__builtin_return_address(0) << frg::endlog;
+}
+
+extern "C" [[ gnu::visibility("hidden") ]] int __cxa_guard_acquire(int64_t *ptr) {
+ auto guard = reinterpret_cast<Guard *>(ptr);
+ guard->lock();
+ // relaxed ordering is sufficient because
+ // Guard::complete is only modified while the mutex is held.
+ if(__atomic_load_n(&guard->complete, __ATOMIC_RELAXED)) {
+ guard->unlock();
+ return 0;
+ }else{
+ return 1;
+ }
+}
+
+extern "C" [[ gnu::visibility("hidden") ]] void __cxa_guard_release(int64_t *ptr) {
+ auto guard = reinterpret_cast<Guard *>(ptr);
+ // do a store-release so that compiler generated code can skip calling
+ // __cxa_guard_acquire by doing a load-acquire on Guard::complete.
+ __atomic_store_n(&guard->complete, 1, __ATOMIC_RELEASE);
+ guard->unlock();
+}
+
diff --git a/lib/mlibc/options/internal/gcc/initfini.cpp b/lib/mlibc/options/internal/gcc/initfini.cpp
new file mode 100644
index 0000000..b329f38
--- /dev/null
+++ b/lib/mlibc/options/internal/gcc/initfini.cpp
@@ -0,0 +1,23 @@
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <mlibc/internal-sysdeps.hpp>
+#include <mlibc/debug.hpp>
+
+typedef void (*InitPtr)();
+
+extern InitPtr __CTOR_LIST__ [[ gnu::visibility("hidden") ]];
+extern InitPtr __CTOR_END__ [[ gnu::visibility("hidden") ]];
+
+extern "C" [[ gnu::visibility("hidden") ]] void __mlibc_do_ctors() {
+ auto it = &__CTOR_LIST__;
+ while(it != &__CTOR_END__)
+ (*it++)();
+}
+
+extern "C" [[ gnu::visibility("hidden") ]] void __mlibc_do_dtors() {
+ mlibc::sys_libc_log("__mlibc_do_dtors() called");
+}
+
diff --git a/lib/mlibc/options/internal/gcc/stack_protector.cpp b/lib/mlibc/options/internal/gcc/stack_protector.cpp
new file mode 100644
index 0000000..e5e50f0
--- /dev/null
+++ b/lib/mlibc/options/internal/gcc/stack_protector.cpp
@@ -0,0 +1,31 @@
+#include <stdint.h>
+#include <string.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/stack_protector.hpp>
+
+uintptr_t __stack_chk_guard = 0;
+
+namespace mlibc {
+
+void initStackGuard(void *entropy) {
+ if(entropy != nullptr) {
+ memcpy(&__stack_chk_guard, entropy, sizeof(__stack_chk_guard));
+ } else {
+ // If no entropy is available, set it to the terminator canary
+ __stack_chk_guard = 0;
+ __stack_chk_guard |= ('\n' << 16);
+ __stack_chk_guard |= (255 << 24);
+ }
+}
+
+} // namespace mlibc
+
+extern "C" [[noreturn]] void __stack_chk_fail() {
+ mlibc::panicLogger() << "Stack smashing detected!" << frg::endlog;
+ __builtin_unreachable();
+}
+
+extern "C" [[noreturn, gnu::visibility("hidden")]] void __stack_chk_fail_local() {
+ __stack_chk_fail();
+};
+
diff --git a/lib/mlibc/options/internal/generic/allocator.cpp b/lib/mlibc/options/internal/generic/allocator.cpp
new file mode 100644
index 0000000..d738212
--- /dev/null
+++ b/lib/mlibc/options/internal/generic/allocator.cpp
@@ -0,0 +1,196 @@
+
+#include <string.h>
+
+#include <bits/ensure.h>
+#include <frg/eternal.hpp>
+#include <mlibc/allocator.hpp>
+#include <mlibc/internal-sysdeps.hpp>
+#include <internal-config.h>
+
+#if !MLIBC_DEBUG_ALLOCATOR
+
+// --------------------------------------------------------
+// Globals
+// --------------------------------------------------------
+
+MemoryAllocator &getAllocator() {
+ // 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();
+}
+
+// --------------------------------------------------------
+// VirtualAllocator
+// --------------------------------------------------------
+
+uintptr_t VirtualAllocator::map(size_t length) {
+ void *ptr;
+ __ensure(!mlibc::sys_anon_allocate(length, &ptr));
+ return (uintptr_t)ptr;
+}
+
+void VirtualAllocator::unmap(uintptr_t address, size_t length) {
+ __ensure(!mlibc::sys_anon_free((void *)address, length));
+}
+
+#else
+
+namespace {
+ struct AllocatorMeta {
+ size_t allocatedSize;
+ size_t pagesSize;
+ frg::array<uint64_t, 4> magic;
+ };
+
+ constexpr frg::array<uint64_t, 4> allocatorMagic {
+ 0x6d4bbb9f3446e83f, 0x25e213a7a7f9f954,
+ 0x1a3c667586538bef, 0x994f34ff71c090bc
+ };
+} // namespace anonymous
+
+// Turn vm_unmap calls in free into vm_map(..., PROT_NONE, ...) calls to prevent
+// those addresses from being reused. This is useful for detecting situations like this:
+// 1. Allocate object X at address Y
+// 2. Do some computation using object X
+// 3. Free object X at address Y
+// 4. Allocate object Z at address W, and it so happens that W == Y
+// 5. Try to use object X, but the memory which was backing it now contains object Z
+constexpr bool neverReleaseVa = false;
+constexpr bool logAllocations = false;
+
+// Area before the returned allocated block (which exists due to us offseting
+// the block to be as close to the edge of a page).
+constexpr uint8_t offsetAreaValue = 'A';
+// Area which we return a pointer to in allocate and reallocate.
+constexpr uint8_t allocatedAreaValue = 'B';
+// Area after the allocated block, which exists due to the alignment constraints.
+constexpr uint8_t alignmentAreaValue = 'C';
+// Remaining area within the metadata page after the metadata.
+constexpr uint8_t metaAreaValue = 'D';
+
+// Alignment of the returned memory.
+// TODO(qookie): Eventually accept alignment as an argument of allocate.
+constexpr size_t pointerAlignment = 16;
+
+// TODO(qookie): Support this. Perhaps by overallocating by 2x and then picking
+// an offset that guarantees the desired alignment.
+static_assert(pointerAlignment <= 4096, "Pointer aligment of more than 4096 bytes is unsupported");
+static_assert(!(pointerAlignment & (pointerAlignment - 1)),
+ "Pointer aligment must be a power of 2");
+
+constexpr size_t pageSize = 0x1000;
+
+void *MemoryAllocator::allocate(size_t size) {
+ size_t pg_size = (size + size_t{pageSize - 1}) & ~size_t{pageSize - 1};
+ size_t offset = (pg_size - size) & ~size_t{pointerAlignment - 1};
+
+ void *ptr;
+
+ // Two extra pages for metadata in front and guard page at the end
+ // Reserve the whole region as PROT_NONE...
+ if (int e = mlibc::sys_vm_map(nullptr, pg_size + pageSize * 2, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0, &ptr))
+ mlibc::panicLogger() << "sys_vm_map failed in MemoryAllocator::allocate (errno " << e << ")" << frg::endlog;
+
+ // ...Then replace pages to make them accessible, excluding the guard page
+ if (int e = mlibc::sys_vm_map(ptr, pg_size + pageSize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0, &ptr))
+ mlibc::panicLogger() << "sys_vm_map failed in MemoryAllocator::allocate (errno " << e << ")" << frg::endlog;
+
+ void *meta = ptr;
+ void *out_page = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(ptr) + pageSize);
+ void *out = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(out_page) + offset);
+ void *out_align_area = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(out) + size);
+
+ AllocatorMeta metaData{size, pg_size, allocatorMagic};
+
+ memset(meta, metaAreaValue, pageSize);
+ memcpy(meta, &metaData, sizeof(AllocatorMeta));
+
+ memset(out_page, offsetAreaValue, offset);
+ memset(out, allocatedAreaValue, size);
+ memset(out_align_area, alignmentAreaValue, pg_size - offset - size);
+
+ if constexpr (logAllocations)
+ mlibc::infoLogger() << "MemoryAllocator::allocate(" << size << ") = " << out << frg::endlog;
+
+ return out;
+}
+
+void MemoryAllocator::free(void *ptr) {
+ if (!ptr)
+ return;
+
+ if constexpr (logAllocations)
+ mlibc::infoLogger() << "MemoryAllocator::free(" << ptr << ")" << frg::endlog;
+
+ uintptr_t page_addr = reinterpret_cast<uintptr_t>(ptr) & ~size_t{pageSize - 1};
+ AllocatorMeta *meta = reinterpret_cast<AllocatorMeta *>(page_addr - pageSize);
+
+ if (meta->magic != allocatorMagic)
+ mlibc::panicLogger() << "Invalid allocator metadata magic in MemoryAllocator::free" << frg::endlog;
+
+ deallocate(ptr, meta->allocatedSize);
+}
+
+void MemoryAllocator::deallocate(void *ptr, size_t size) {
+ if (!ptr)
+ return;
+
+ if constexpr (logAllocations)
+ mlibc::infoLogger() << "MemoryAllocator::deallocate(" << ptr << ", " << size << ")" << frg::endlog;
+
+ uintptr_t page_addr = reinterpret_cast<uintptr_t>(ptr) & ~size_t{pageSize - 1};
+ AllocatorMeta *meta = reinterpret_cast<AllocatorMeta *>(page_addr - pageSize);
+
+ if (meta->magic != allocatorMagic)
+ mlibc::panicLogger() << "Invalid allocator metadata magic in MemoryAllocator::deallocate" << frg::endlog;
+
+ if (size != meta->allocatedSize)
+ mlibc::panicLogger() << "Invalid allocated size in metadata in MemoryAllocator::deallocate (given " << size << ", stored " << meta->allocatedSize << ")" << frg::endlog;
+
+ if constexpr (neverReleaseVa) {
+ void *unused;
+ if (int e = mlibc::sys_vm_map(meta, meta->pagesSize + pageSize * 2, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0, &unused))
+ mlibc::panicLogger() << "sys_vm_map failed in MemoryAllocator::deallocate (errno " << e << ")" << frg::endlog;
+ } else {
+ if (int e = mlibc::sys_vm_unmap(meta, meta->pagesSize + pageSize * 2))
+ mlibc::panicLogger() << "sys_vm_unmap failed in MemoryAllocator::deallocate (errno " << e << ")" << frg::endlog;
+ }
+}
+
+void *MemoryAllocator::reallocate(void *ptr, size_t size) {
+ if (!size) {
+ free(ptr);
+ return nullptr;
+ }
+
+ void *newArea = allocate(size);
+
+ if (ptr) {
+ uintptr_t page_addr = reinterpret_cast<uintptr_t>(ptr) & ~size_t{pageSize - 1};
+ AllocatorMeta *meta = reinterpret_cast<AllocatorMeta *>(page_addr - pageSize);
+
+ if (meta->magic != allocatorMagic)
+ mlibc::panicLogger() << "Invalid allocator metadata magic in MemoryAllocator::reallocate" << frg::endlog;
+
+ memcpy(newArea, ptr, frg::min(meta->allocatedSize, size));
+
+ deallocate(ptr, meta->allocatedSize);
+ }
+
+ if constexpr (logAllocations)
+ mlibc::infoLogger() << "MemoryAllocator::reallocate(" << ptr << ", " << size << ") = " << newArea << frg::endlog;
+
+ return newArea;
+}
+
+MemoryAllocator &getAllocator() {
+ // use frg::eternal to prevent a call to __cxa_atexit().
+ // this is necessary because __cxa_atexit() call this function.
+ static frg::eternal<MemoryAllocator> singleton{};
+ return singleton.get();
+}
+
+#endif /* !MLIBC_DEBUG_ALLOCATOR */
diff --git a/lib/mlibc/options/internal/generic/charcode.cpp b/lib/mlibc/options/internal/generic/charcode.cpp
new file mode 100644
index 0000000..e09d5cd
--- /dev/null
+++ b/lib/mlibc/options/internal/generic/charcode.cpp
@@ -0,0 +1,244 @@
+
+#include <bits/ensure.h>
+#include <frg/string.hpp>
+#include <mlibc/charcode.hpp>
+#include <mlibc/debug.hpp>
+
+namespace mlibc {
+
+struct utf8_charcode {
+ static constexpr bool preserves_7bit_units = true;
+ static constexpr bool has_shift_states = false;
+
+ struct decode_state {
+ decode_state()
+ : _progress{0}, _cpoint{0} { }
+
+ auto progress() { return _progress; }
+ auto cpoint() { return _cpoint; }
+
+ charcode_error operator() (code_seq<const char> &seq) {
+ auto uc = static_cast<unsigned char>(*seq.it);
+ if(!_progress) {
+ if(!(uc & 0b1000'0000)) {
+ // ASCII-compatible.
+ _cpoint = uc;
+ }else if((uc & 0b1110'0000) == 0b1100'0000) {
+ _cpoint = uc & 0b1'1111;
+ _progress = 1;
+ }else if((uc & 0b1111'0000) == 0b1110'0000) {
+ _cpoint = uc & 0b1111;
+ _progress = 2;
+ }else if((uc & 0b1111'1000) == 0b1111'0000) {
+ _cpoint = uc & 0b111;
+ _progress = 3;
+ }else{
+ // If the highest two bits are 0b10, this is the second (or later) unit.
+ // Units with highest five bits = 0b11111 do not occur in valid UTF-8.
+ __ensure((uc & 0b1100'0000) == 0b1000'0000
+ || (uc & 0b1111'1000) == 0b1111'1000);
+ return charcode_error::illegal_input;
+ }
+ }else{
+ // TODO: Return an error.
+ __ensure((uc & 0b1100'0000) == 0b1000'0000);
+ _cpoint = (_cpoint << 6) | (uc & 0x3F);
+ --_progress;
+ }
+ ++seq.it;
+ return charcode_error::null;
+ }
+
+ private:
+ int _progress;
+ codepoint _cpoint;
+ };
+
+ struct encode_state {
+ // Encodes a single character from wseq + the current state and stores it in nseq.
+ // TODO: Convert decode_state to the same strategy.
+ charcode_error operator() (code_seq<char> &nseq, code_seq<const codepoint> &wseq) {
+ auto wc = *wseq.it;
+ __ensure(wc <= 0x7F && "utf8_charcode cannot encode multibyte chars yet");
+ *nseq.it = wc;
+ ++wseq.it;
+ ++nseq.it;
+ return charcode_error::null;
+ }
+ };
+};
+
+polymorphic_charcode::~polymorphic_charcode() = default;
+
+// For *decoding, this class assumes that:
+// - G::decode_state has members progress() and cpoint().
+// - G::decode_state::progress() >= 0 at all times.
+// TODO: This will be needed on platforms like Windows, where wchar_t is UTF-16.
+// TODO: There, we can use negative __mlibc_mbstate::progress to represent encoding to UTF-16.
+// - If G::decode_state::progress() == 0, the code point (given by cpoint())
+// was decoded successfully.
+template<typename G>
+struct polymorphic_charcode_adapter : polymorphic_charcode {
+ polymorphic_charcode_adapter()
+ : polymorphic_charcode{G::preserves_7bit_units, G::has_shift_states} { }
+
+ charcode_error decode(code_seq<const char> &nseq, code_seq<codepoint> &wseq,
+ __mlibc_mbstate &st) override {
+ __ensure(!st.__progress); // TODO: Update st with ds.progress() and ds.cpoint().
+
+ code_seq<const char> decode_nseq = nseq;
+ typename G::decode_state ds;
+
+ while(decode_nseq && wseq) {
+ // Consume the next code unit.
+ if(auto e = ds(decode_nseq); e != charcode_error::null)
+ return e;
+
+ // Produce a new code point.
+ if(!ds.progress()) {
+ // "Commit" consumed code units (as there was no decode error).
+ nseq.it = decode_nseq.it;
+ if(!ds.cpoint()) // Stop on null characters.
+ return charcode_error::null;
+ *wseq.it = ds.cpoint();
+ ++wseq.it;
+ }
+ }
+
+ if(ds.progress())
+ return charcode_error::input_underflow;
+ return charcode_error::null;
+ }
+
+ charcode_error decode_wtranscode(code_seq<const char> &nseq, code_seq<wchar_t> &wseq,
+ __mlibc_mbstate &st) override {
+ __ensure(!st.__progress); // TODO: Update st with ds.progress() and ds.cpoint().
+
+ code_seq<const char> decode_nseq = nseq;
+ typename G::decode_state ds;
+
+ while(decode_nseq && wseq) {
+ // Consume the next code unit.
+ if(auto e = ds(decode_nseq); e != charcode_error::null)
+ return e;
+
+ // Produce a new code point.
+ if(!ds.progress()) {
+ nseq.it = decode_nseq.it;
+ // "Commit" consumed code units (as there was no decode error).
+ if(!ds.cpoint()) // Stop on null characters.
+ return charcode_error::null;
+ *wseq.it = ds.cpoint();
+ ++wseq.it;
+ }
+ }
+
+ if(ds.progress())
+ return charcode_error::input_underflow;
+ return charcode_error::null;
+ }
+
+ charcode_error decode_wtranscode_length(code_seq<const char> &nseq, size_t *n,
+ __mlibc_mbstate &st) override {
+ __ensure(!st.__progress); // TODO: Update st with ds.progress() and ds.cpoint().
+
+ code_seq<const char> decode_nseq = nseq;
+ typename G::decode_state ds;
+
+ *n = 0;
+ while(decode_nseq) {
+ // Consume the next code unit.
+ if(auto e = ds(decode_nseq); e != charcode_error::null)
+ return e;
+
+ if(!ds.progress()) {
+ nseq.it = decode_nseq.it;
+ // "Commit" consumed code units (as there was no decode error).
+ if(!ds.cpoint()) // Stop on null code points.
+ return charcode_error::null;
+ ++(*n);
+ }
+ }
+
+ if(ds.progress())
+ return charcode_error::input_underflow;
+ return charcode_error::null;
+ }
+
+ charcode_error encode_wtranscode(code_seq<char> &nseq, code_seq<const wchar_t> &wseq,
+ __mlibc_mbstate &st) override {
+ __ensure(!st.__progress); // TODO: Update st with es.progress() and es.cpoint().
+
+ code_seq<char> encode_nseq = nseq;
+ typename G::encode_state es;
+
+ while(encode_nseq && wseq) {
+ codepoint cp = *wseq.it;
+ if(!cp)
+ return charcode_error::null;
+
+ code_seq<const codepoint> cps{&cp, &cp + 1};
+ if(auto e = es(encode_nseq, cps); e == charcode_error::dirty) {
+ continue;
+ }else if(e != charcode_error::null) {
+ return e;
+ }
+ __ensure(cps.it == cps.end);
+ ++wseq.it;
+
+ // "Commit" produced code units (as there was no encode error).
+ nseq.it = encode_nseq.it;
+ }
+
+ if(encode_nseq.it != nseq.it)
+ return charcode_error::output_overflow;
+ return charcode_error::null;
+ }
+
+ charcode_error encode_wtranscode_length(code_seq<const wchar_t> &wseq, size_t *n,
+ __mlibc_mbstate &st) override {
+ __ensure(!st.__progress); // TODO: Update st with es.progress() and es.cpoint().
+
+ typename G::encode_state es;
+
+ *n = 0;
+ while(wseq) {
+ char temp[4];
+ code_seq<char> encode_nseq{temp, temp + 4};
+ codepoint cp = *wseq.it;
+ if(!cp)
+ return charcode_error::null;
+ // Consume the next code unit.
+ code_seq<const codepoint> cps{&cp, &cp + 1};
+ if(auto e = es(encode_nseq, cps); e == charcode_error::dirty) {
+ continue;
+ }else if(e != charcode_error::null) {
+ return e;
+ }
+
+ ++(*n);
+ ++wseq.it;
+ }
+
+ return charcode_error::null;
+ }
+};
+
+polymorphic_charcode *current_charcode() {
+ static polymorphic_charcode_adapter<utf8_charcode> global_charcode;
+ return &global_charcode;
+}
+
+charcode_error wide_charcode::promote(wchar_t nc, codepoint &wc) {
+ // TODO: Allow non-identity encodings of wchar_t.
+ wc = nc;
+ return charcode_error::null;
+}
+
+wide_charcode *platform_wide_charcode() {
+ static wide_charcode global_wide_charcode;
+ return &global_wide_charcode;
+}
+
+} // namespace mlibc
+
diff --git a/lib/mlibc/options/internal/generic/charset.cpp b/lib/mlibc/options/internal/generic/charset.cpp
new file mode 100644
index 0000000..c42b4f4
--- /dev/null
+++ b/lib/mlibc/options/internal/generic/charset.cpp
@@ -0,0 +1,144 @@
+
+#include <bits/ensure.h>
+#include <mlibc/charset.hpp>
+#include <mlibc/debug.hpp>
+
+namespace mlibc {
+
+bool charset::is_ascii_superset() {
+ // TODO: For locales that change the meaning of ASCII chars, this needs to be changed.
+ return true;
+}
+
+bool charset::is_alpha(codepoint c) {
+ if(c <= 0x7F && is_ascii_superset())
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+ if(c > 0x7F)
+ mlibc::infoLogger() << "mlibc: charset::is_alpha() is not implemented"
+ " for the full Unicode charset" << frg::endlog;
+ return false;
+}
+
+bool charset::is_digit(codepoint c) {
+ if(c <= 0x7F && is_ascii_superset())
+ return c >= '0' && c <= '9';
+ if(c > 0x7F)
+ mlibc::infoLogger() << "mlibc: charset::is_digit() is not implemented"
+ " for the full Unicode charset" << frg::endlog;
+ return false;
+}
+
+bool charset::is_xdigit(codepoint c) {
+ if(c <= 0x7F && is_ascii_superset())
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+ if(c > 0x7F)
+ mlibc::infoLogger() << "mlibc: charset::is_xdigit() is not implemented"
+ " for the full Unicode charset" << frg::endlog;
+ return false;
+}
+
+bool charset::is_alnum(codepoint c) {
+ if(c <= 0x7F && is_ascii_superset())
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+ if(c > 0x7F)
+ mlibc::infoLogger() << "mlibc: charset::is_alnum() is not implemented"
+ " for the full Unicode charset" << frg::endlog;
+ return false;
+}
+
+bool charset::is_punct(codepoint c) {
+ if(c <= 0x7F && is_ascii_superset())
+ return c == '!' || c == '"' || c == '#' || c == '$' || c == '%' || c == '&'
+ || c == '\'' || c == '(' || c == ')' || c == '*' || c == '+' || c == ','
+ || c == '-' || c == '.' || c == '/'
+ || c == ':' || c == ';' || c == '<' || c == '=' || c == '>' || c == '?'
+ || c == '@'
+ || c == '[' || c == '\\' || c == ']' || c == '^' || c == '_' || c == '`'
+ || c == '{' || c == '|' || c == '}' || c == '~';
+ if(c > 0x7F)
+ mlibc::infoLogger() << "mlibc: charset::is_punct() is not implemented"
+ " for the full Unicode charset" << frg::endlog;
+ return false;
+}
+
+bool charset::is_graph(codepoint c) {
+ if(c <= 0x7F && is_ascii_superset())
+ return c >= 0x21 && c <= 0x7E;
+ if(c > 0x7F)
+ mlibc::infoLogger() << "mlibc: charset::is_graph() is not implemented"
+ " for the full Unicode charset" << frg::endlog;
+ return false;
+}
+
+bool charset::is_blank(codepoint c) {
+ if(c <= 0x7F && is_ascii_superset())
+ return c == ' ' || c == '\t';
+ if(c > 0x7F)
+ mlibc::infoLogger() << "mlibc: charset::is_blank() is not implemented"
+ " for the full Unicode charset " << c << frg::endlog;
+ return false;
+}
+
+bool charset::is_space(codepoint c) {
+ if(c <= 0x7F && is_ascii_superset())
+ return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
+ if(c > 0x7F)
+ mlibc::infoLogger() << "mlibc: charset::is_space() is not implemented"
+ " for the full Unicode charset" << frg::endlog;
+ return false;
+}
+
+bool charset::is_print(codepoint c) {
+ if(c <= 0x7F && is_ascii_superset())
+ return c >= 0x20 && c <= 0x7E;
+ if(c > 0x7F)
+ mlibc::infoLogger() << "mlibc: charset::is_print() is not implemented"
+ " for the full Unicode charset" << frg::endlog;
+ return false;
+}
+
+bool charset::is_lower(codepoint c) {
+ if(c <= 0x7F && is_ascii_superset())
+ return (c >= 'a' && c <= 'z');
+ if(c > 0x7F)
+ mlibc::infoLogger() << "mlibc: charset::is_print() is not implemented"
+ " for the full Unicode charset" << frg::endlog;
+ return false;
+}
+
+bool charset::is_upper(codepoint c) {
+ if(c <= 0x7F && is_ascii_superset())
+ return (c >= 'A' && c <= 'Z');
+ if(c > 0x7F)
+ mlibc::infoLogger() << "mlibc: charset::is_print() is not implemented"
+ " for the full Unicode charset" << frg::endlog;
+ return false;
+}
+
+codepoint charset::to_lower(codepoint c) {
+ if(c <= 0x7F && is_ascii_superset())
+ if(c >= 'A' && c <= 'Z')
+ return c - 'A' + 'a';
+ if(c > 0x7F)
+ mlibc::infoLogger() << "mlibc: charset::to_lower() is not implemented"
+ " for the full Unicode charset" << frg::endlog;
+ return c;
+}
+
+codepoint charset::to_upper(codepoint c) {
+ if(c <= 0x7F && is_ascii_superset())
+ if(c >= 'a' && c <= 'z')
+ return c - 'a' + 'A';
+ if(c > 0x7F)
+ mlibc::infoLogger() << "mlibc: charset::to_upper() is not implemented"
+ " for the full Unicode charset" << frg::endlog;
+ return c;
+}
+
+charset *current_charset() {
+ static charset global_charset;
+ return &global_charset;
+}
+
+} // namespace mlibc
+
diff --git a/lib/mlibc/options/internal/generic/debug.cpp b/lib/mlibc/options/internal/generic/debug.cpp
new file mode 100644
index 0000000..19427c8
--- /dev/null
+++ b/lib/mlibc/options/internal/generic/debug.cpp
@@ -0,0 +1,22 @@
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/internal-sysdeps.hpp>
+
+namespace mlibc {
+
+frg::stack_buffer_logger<InfoSink, 512> infoLogger;
+frg::stack_buffer_logger<PanicSink, 512> panicLogger;
+
+void InfoSink::operator() (const char *message) {
+ sys_libc_log(message);
+}
+
+void PanicSink::operator() (const char *message) {
+// sys_libc_log("mlibc: Write to PanicSink");
+ sys_libc_log(message);
+ sys_libc_panic();
+}
+
+} // namespace mlibc
+
diff --git a/lib/mlibc/options/internal/generic/ensure.cpp b/lib/mlibc/options/internal/generic/ensure.cpp
new file mode 100644
index 0000000..57c953a
--- /dev/null
+++ b/lib/mlibc/options/internal/generic/ensure.cpp
@@ -0,0 +1,18 @@
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+
+void __ensure_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;
+}
+
+void __ensure_warn(const char *assertion, const char *file, unsigned int line,
+ const char *function) {
+ mlibc::infoLogger() << "In function " << function
+ << ", file " << file << ":" << line << "\n"
+ << "__ensure(" << assertion << ") failed" << frg::endlog;
+}
+
diff --git a/lib/mlibc/options/internal/generic/essential.cpp b/lib/mlibc/options/internal/generic/essential.cpp
new file mode 100644
index 0000000..d00df1e
--- /dev/null
+++ b/lib/mlibc/options/internal/generic/essential.cpp
@@ -0,0 +1,217 @@
+#include <string.h>
+#include <stdint.h>
+
+namespace {
+ // Needed since we cannot declare a templated enum.
+ template<typename T>
+ struct word_helper {
+ using underlying [[gnu::aligned(1)]] = T;
+ enum class [[gnu::may_alias]] word_enum : underlying { };
+ };
+
+ template<typename T>
+ using word = typename word_helper<T>::word_enum;
+
+ template<typename T>
+ [[gnu::always_inline]]
+ inline word<T> alias_load(const unsigned char *&p) {
+ word<T> value = *reinterpret_cast<const word<T> *>(p);
+ p += sizeof(T);
+ return value;
+ }
+
+ template<typename T>
+ [[gnu::always_inline]]
+ inline void alias_store(unsigned char *&p, word<T> value) {
+ *reinterpret_cast<word<T> *>(p) = value;
+ p += sizeof(T);
+ }
+
+#ifdef __LP64__
+ void *forward_copy(void *__restrict dest, const void *__restrict src, size_t n) {
+ auto curDest = reinterpret_cast<unsigned char *>(dest);
+ auto curSrc = reinterpret_cast<const unsigned char *>(src);
+
+ while(n >= 8 * 8) {
+ auto w1 = alias_load<uint64_t>(curSrc);
+ auto w2 = alias_load<uint64_t>(curSrc);
+ auto w3 = alias_load<uint64_t>(curSrc);
+ auto w4 = alias_load<uint64_t>(curSrc);
+ auto w5 = alias_load<uint64_t>(curSrc);
+ auto w6 = alias_load<uint64_t>(curSrc);
+ auto w7 = alias_load<uint64_t>(curSrc);
+ auto w8 = alias_load<uint64_t>(curSrc);
+ alias_store<uint64_t>(curDest, w1);
+ alias_store<uint64_t>(curDest, w2);
+ alias_store<uint64_t>(curDest, w3);
+ alias_store<uint64_t>(curDest, w4);
+ alias_store<uint64_t>(curDest, w5);
+ alias_store<uint64_t>(curDest, w6);
+ alias_store<uint64_t>(curDest, w7);
+ alias_store<uint64_t>(curDest, w8);
+ n -= 8 * 8;
+ }
+ if(n >= 4 * 8) {
+ auto w1 = alias_load<uint64_t>(curSrc);
+ auto w2 = alias_load<uint64_t>(curSrc);
+ auto w3 = alias_load<uint64_t>(curSrc);
+ auto w4 = alias_load<uint64_t>(curSrc);
+ alias_store<uint64_t>(curDest, w1);
+ alias_store<uint64_t>(curDest, w2);
+ alias_store<uint64_t>(curDest, w3);
+ alias_store<uint64_t>(curDest, w4);
+ n -= 4 * 8;
+ }
+ if(n >= 2 * 8) {
+ auto w1 = alias_load<uint64_t>(curSrc);
+ auto w2 = alias_load<uint64_t>(curSrc);
+ alias_store<uint64_t>(curDest, w1);
+ alias_store<uint64_t>(curDest, w2);
+ n -= 2 * 8;
+ }
+ if(n >= 8) {
+ auto w = alias_load<uint64_t>(curSrc);
+ alias_store<uint64_t>(curDest, w);
+ n -= 8;
+ }
+ if(n >= 4) {
+ auto w = alias_load<uint32_t>(curSrc);
+ alias_store<uint32_t>(curDest, w);
+ n -= 4;
+ }
+ if(n >= 2) {
+ auto w = alias_load<uint16_t>(curSrc);
+ alias_store<uint16_t>(curDest, w);
+ n -= 2;
+ }
+ if(n)
+ *curDest = *curSrc;
+ return dest;
+ }
+#else // !__LP64__
+ void *forward_copy(void *dest, const void *src, size_t n) {
+ for(size_t i = 0; i < n; i++)
+ ((char *)dest)[i] = ((const char *)src)[i];
+ return dest;
+ }
+#endif // __LP64__ / !__LP64__
+}
+
+// --------------------------------------------------------------------------------------
+// memcpy() implementation.
+// --------------------------------------------------------------------------------------
+
+
+void *memcpy(void *__restrict dest, const void *__restrict src, size_t n) {
+ return forward_copy(dest, src, n);
+}
+
+
+// --------------------------------------------------------------------------------------
+// memset() implementation.
+// --------------------------------------------------------------------------------------
+
+#ifdef __LP64__
+
+void *memset(void *dest, int val, size_t n) {
+ auto curDest = reinterpret_cast<unsigned char *>(dest);
+ unsigned char byte = val;
+
+ // Get rid of misalignment.
+ while(n && (reinterpret_cast<uintptr_t>(curDest) & 7)) {
+ *curDest++ = byte;
+ --n;
+ }
+
+ auto pattern64 = static_cast<word<uint64_t>>(
+ static_cast<uint64_t>(byte)
+ | (static_cast<uint64_t>(byte) << 8)
+ | (static_cast<uint64_t>(byte) << 16)
+ | (static_cast<uint64_t>(byte) << 24)
+ | (static_cast<uint64_t>(byte) << 32)
+ | (static_cast<uint64_t>(byte) << 40)
+ | (static_cast<uint64_t>(byte) << 48)
+ | (static_cast<uint64_t>(byte) << 56));
+
+ auto pattern32 = static_cast<word<uint32_t>>(
+ static_cast<uint32_t>(byte)
+ | (static_cast<uint32_t>(byte) << 8)
+ | (static_cast<uint32_t>(byte) << 16)
+ | (static_cast<uint32_t>(byte) << 24));
+
+ auto pattern16 = static_cast<word<uint16_t>>(
+ static_cast<uint16_t>(byte)
+ | (static_cast<uint16_t>(byte) << 8));
+
+ while(n >= 8 * 8) {
+ alias_store<uint64_t>(curDest, pattern64);
+ alias_store<uint64_t>(curDest, pattern64);
+ alias_store<uint64_t>(curDest, pattern64);
+ alias_store<uint64_t>(curDest, pattern64);
+ alias_store<uint64_t>(curDest, pattern64);
+ alias_store<uint64_t>(curDest, pattern64);
+ alias_store<uint64_t>(curDest, pattern64);
+ alias_store<uint64_t>(curDest, pattern64);
+ n -= 8 * 8;
+ }
+ if(n >= 4 * 8) {
+ alias_store<uint64_t>(curDest, pattern64);
+ alias_store<uint64_t>(curDest, pattern64);
+ alias_store<uint64_t>(curDest, pattern64);
+ alias_store<uint64_t>(curDest, pattern64);
+ n -= 4 * 8;
+ }
+ if(n >= 2 * 8) {
+ alias_store<uint64_t>(curDest, pattern64);
+ alias_store<uint64_t>(curDest, pattern64);
+ n -= 2 * 8;
+ }
+ if(n >= 8) {
+ alias_store<uint64_t>(curDest, pattern64);
+ n -= 8;
+ }
+ if(n >= 4) {
+ alias_store<uint32_t>(curDest, pattern32);
+ n -= 4;
+ }
+ if(n >= 2) {
+ alias_store<uint16_t>(curDest, pattern16);
+ n -= 2;
+ }
+ if(n)
+ *curDest = byte;
+ return dest;
+}
+
+#else // !__LP64__
+
+void *memset(void *dest, int byte, size_t count) {
+ for(size_t i = 0; i < count; i++)
+ ((char *)dest)[i] = (char)byte;
+ return dest;
+}
+
+#endif // __LP64__ / !__LP64__
+
+// --------------------------------------------------------------------------------------
+// "Non-optimized" functions.
+// --------------------------------------------------------------------------------------
+
+void *memmove(void *dest, const void *src, size_t size) {
+ char *dest_bytes = (char *)dest;
+ char *src_bytes = (char *)src;
+ if(dest_bytes < src_bytes) {
+ return forward_copy(dest, src, size);
+ }else if(dest_bytes > src_bytes) {
+ for(size_t i = 0; i < size; i++)
+ dest_bytes[size - i - 1] = src_bytes[size - i - 1];
+ }
+ return dest;
+}
+
+size_t strlen(const char *s) {
+ size_t len = 0;
+ for(size_t i = 0; s[i]; i++)
+ len++;
+ return len;
+}
diff --git a/lib/mlibc/options/internal/generic/frigg.cpp b/lib/mlibc/options/internal/generic/frigg.cpp
new file mode 100644
index 0000000..7575c9c
--- /dev/null
+++ b/lib/mlibc/options/internal/generic/frigg.cpp
@@ -0,0 +1,14 @@
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/internal-sysdeps.hpp>
+
+extern "C" void frg_panic(const char *mstr) {
+// mlibc::sys_libc_log("mlibc: Call to frg_panic");
+ mlibc::sys_libc_log(mstr);
+ mlibc::sys_libc_panic();
+}
+
+extern "C" void frg_log(const char *mstr) {
+ mlibc::sys_libc_log(mstr);
+}
diff --git a/lib/mlibc/options/internal/generic/global-config.cpp b/lib/mlibc/options/internal/generic/global-config.cpp
new file mode 100644
index 0000000..264a984
--- /dev/null
+++ b/lib/mlibc/options/internal/generic/global-config.cpp
@@ -0,0 +1,27 @@
+#include <stdlib.h>
+#include <string.h>
+#include <mlibc/global-config.hpp>
+
+namespace mlibc {
+
+struct GlobalConfigGuard {
+ GlobalConfigGuard();
+};
+
+GlobalConfigGuard guard;
+
+GlobalConfigGuard::GlobalConfigGuard() {
+ // Force the config to be created during initialization of libc.so.
+ mlibc::globalConfig();
+}
+
+static bool envEnabled(const char *env) {
+ auto value = getenv(env);
+ return value && *value && *value != '0';
+}
+
+GlobalConfig::GlobalConfig() {
+ debugMalloc = envEnabled("MLIBC_DEBUG_MALLOC");
+}
+
+}
diff --git a/lib/mlibc/options/internal/generic/inline-emitter.cpp b/lib/mlibc/options/internal/generic/inline-emitter.cpp
new file mode 100644
index 0000000..bf81c0b
--- /dev/null
+++ b/lib/mlibc/options/internal/generic/inline-emitter.cpp
@@ -0,0 +1,16 @@
+// This translation unit provides symbols for functions marked with __MLIBC_INLINE_DEFINITION.
+// All headers with such functions must be included here.
+
+#define __MLIBC_EMIT_INLINE_DEFINITIONS
+
+#include <mlibc-config.h>
+
+#include <elf.h>
+
+#if __MLIBC_LINUX_OPTION
+#include <sys/sysmacros.h>
+#endif /* __MLIBC_LINUX_OPTION */
+
+#ifndef MLIBC_BUILDING_RTDL
+#include <math.h>
+#endif
diff --git a/lib/mlibc/options/internal/generic/locale.cpp b/lib/mlibc/options/internal/generic/locale.cpp
new file mode 100644
index 0000000..7ba040f
--- /dev/null
+++ b/lib/mlibc/options/internal/generic/locale.cpp
@@ -0,0 +1,87 @@
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/locale.hpp>
+
+namespace mlibc {
+
+char *nl_langinfo(nl_item item) {
+ if(item == CODESET) {
+ return const_cast<char *>("UTF-8");
+ } else if(item >= ABMON_1 && item <= ABMON_12) {
+ switch(item) {
+ case ABMON_1: return const_cast<char *>("Jan");
+ case ABMON_2: return const_cast<char *>("Feb");
+ case ABMON_3: return const_cast<char *>("Mar");
+ case ABMON_4: return const_cast<char *>("Apr");
+ case ABMON_5: return const_cast<char *>("May");
+ case ABMON_6: return const_cast<char *>("Jun");
+ case ABMON_7: return const_cast<char *>("Jul");
+ case ABMON_8: return const_cast<char *>("Aug");
+ case ABMON_9: return const_cast<char *>("Sep");
+ case ABMON_10: return const_cast<char *>("Oct");
+ case ABMON_11: return const_cast<char *>("Nov");
+ case ABMON_12: return const_cast<char *>("Dec");
+ default:
+ __ensure(!"ABMON_* constants don't seem to be contiguous!");
+ __builtin_unreachable();
+ }
+ } else if(item >= MON_1 && item <= MON_12) {
+ switch(item) {
+ case MON_1: return const_cast<char *>("January");
+ case MON_2: return const_cast<char *>("Feburary");
+ case MON_3: return const_cast<char *>("March");
+ case MON_4: return const_cast<char *>("April");
+ case MON_5: return const_cast<char *>("May");
+ case MON_6: return const_cast<char *>("June");
+ case MON_7: return const_cast<char *>("July");
+ case MON_8: return const_cast<char *>("August");
+ case MON_9: return const_cast<char *>("September");
+ case MON_10: return const_cast<char *>("October");
+ case MON_11: return const_cast<char *>("November");
+ case MON_12: return const_cast<char *>("December");
+ default:
+ __ensure(!"MON_* constants don't seem to be contiguous!");
+ __builtin_unreachable();
+ }
+ } else if(item == AM_STR) {
+ return const_cast<char *>("AM");
+ } else if(item == PM_STR) {
+ return const_cast<char *>("PM");
+ } else if(item >= DAY_1 && item <= DAY_7) {
+ switch(item) {
+ case DAY_1: return const_cast<char *>("Sunday");
+ case DAY_2: return const_cast<char *>("Monday");
+ case DAY_3: return const_cast<char *>("Tuesday");
+ case DAY_4: return const_cast<char *>("Wednesday");
+ case DAY_5: return const_cast<char *>("Thursday");
+ case DAY_6: return const_cast<char *>("Friday");
+ case DAY_7: return const_cast<char *>("Saturday");
+ default:
+ __ensure(!"DAY_* constants don't seem to be contiguous!");
+ __builtin_unreachable();
+ }
+ } else if(item >= ABDAY_1 && item <= ABDAY_7) {
+ switch(item) {
+ case ABDAY_1: return const_cast<char *>("Sun");
+ case ABDAY_2: return const_cast<char *>("Mon");
+ case ABDAY_3: return const_cast<char *>("Tue");
+ case ABDAY_4: return const_cast<char *>("Wed");
+ case ABDAY_5: return const_cast<char *>("Thu");
+ case ABDAY_6: return const_cast<char *>("Fri");
+ case ABDAY_7: return const_cast<char *>("Sat");
+ default:
+ __ensure(!"ABDAY_* constants don't seem to be contiguous!");
+ __builtin_unreachable();
+ }
+ }else if(item == D_FMT) {
+ return const_cast<char *>("%m/%d/%y");
+ }else if(item == T_FMT) {
+ return const_cast<char *>("%H:%M:%S");
+ }else{
+ mlibc::infoLogger() << "mlibc: nl_langinfo item "
+ << item << " is not implemented properly" << frg::endlog;
+ return const_cast<char *>("");
+ }
+}
+
+}
diff --git a/lib/mlibc/options/internal/generic/sigset.cpp b/lib/mlibc/options/internal/generic/sigset.cpp
new file mode 100644
index 0000000..134277d
--- /dev/null
+++ b/lib/mlibc/options/internal/generic/sigset.cpp
@@ -0,0 +1,37 @@
+#include <bits/sigset_t.h>
+#include <bits/ensure.h>
+
+int sigemptyset(sigset_t *sigset) {
+ *sigset = 0;
+ return 0;
+}
+
+int sigfillset(sigset_t *sigset) {
+ *sigset = ~sigset_t(0);
+ return 0;
+}
+
+// TODO: Return EINVAL instead of __ensure()ing.
+
+int sigaddset(sigset_t *sigset, int sig) {
+ int signo = sig - 1;
+ // TODO: do not hard code CHAR_BITS
+ __ensure((unsigned int)signo < sizeof(sigset_t) * 8);
+ *sigset |= sigset_t(1) << signo;
+ return 0;
+}
+
+int sigdelset(sigset_t *sigset, int sig) {
+ int signo = sig - 1;
+ // TODO: do not hard code CHAR_BITS
+ __ensure((unsigned int)signo < sizeof(sigset_t) * 8);
+ *sigset &= ~(sigset_t(1) << signo);
+ return 0;
+}
+
+int sigismember(const sigset_t *set, int sig) {
+ int signo = sig - 1;
+ // TODO: do not hard code CHAR_BITS
+ __ensure((unsigned int)signo < sizeof(sigset_t) * 8);
+ return (*set) & (sigset_t(1) << signo);
+}
diff --git a/lib/mlibc/options/internal/generic/strings.cpp b/lib/mlibc/options/internal/generic/strings.cpp
new file mode 100644
index 0000000..ce4f84b
--- /dev/null
+++ b/lib/mlibc/options/internal/generic/strings.cpp
@@ -0,0 +1,22 @@
+#include <ctype.h>
+
+#include <mlibc/strings.hpp>
+
+namespace mlibc {
+
+int strncasecmp(const char *a, const char *b, size_t size) {
+ for(size_t i = 0; i < size; i++) {
+ unsigned char a_byte = tolower(a[i]);
+ unsigned char b_byte = tolower(b[i]);
+ if(!a_byte && !b_byte)
+ return 0;
+ // If only one char is null, one of the following cases applies.
+ if(a_byte < b_byte)
+ return -1;
+ if(a_byte > b_byte)
+ return 1;
+ }
+ return 0;
+}
+
+}
diff --git a/lib/mlibc/options/internal/generic/threads.cpp b/lib/mlibc/options/internal/generic/threads.cpp
new file mode 100644
index 0000000..5f1168c
--- /dev/null
+++ b/lib/mlibc/options/internal/generic/threads.cpp
@@ -0,0 +1,342 @@
+#include <abi-bits/errno.h>
+#include <bits/threads.h>
+#include <bits/ensure.h>
+#include <mlibc/all-sysdeps.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/lock.hpp>
+#include <mlibc/threads.hpp>
+#include <mlibc/tcb.hpp>
+
+extern "C" Tcb *__rtdl_allocateTcb();
+
+namespace mlibc {
+
+int thread_create(struct __mlibc_thread_data **__restrict thread, const struct __mlibc_threadattr *__restrict attrp, void *entry, void *__restrict user_arg, bool returns_int) {
+ auto new_tcb = __rtdl_allocateTcb();
+ pid_t tid;
+ struct __mlibc_threadattr attr = {};
+ if (!attrp)
+ thread_attr_init(&attr);
+ else
+ attr = *attrp;
+
+ if (attr.__mlibc_cpuset)
+ mlibc::infoLogger() << "pthread_create(): cpuset is ignored!" << frg::endlog;
+ if (attr.__mlibc_sigmaskset)
+ mlibc::infoLogger() << "pthread_create(): sigmask is ignored!" << frg::endlog;
+
+ // TODO: due to alignment guarantees, the stackaddr and stacksize might change
+ // when the stack is allocated. Currently this isn't propagated to the TCB,
+ // but it should be.
+ void *stack = attr.__mlibc_stackaddr;
+ if (!mlibc::sys_prepare_stack) {
+ MLIBC_MISSING_SYSDEP();
+ return ENOSYS;
+ }
+ int ret = mlibc::sys_prepare_stack(&stack, entry,
+ user_arg, new_tcb, &attr.__mlibc_stacksize, &attr.__mlibc_guardsize, &new_tcb->stackAddr);
+ if (ret)
+ return ret;
+
+ if (!mlibc::sys_clone) {
+ MLIBC_MISSING_SYSDEP();
+ return ENOSYS;
+ }
+ new_tcb->stackSize = attr.__mlibc_stacksize;
+ new_tcb->guardSize = attr.__mlibc_guardsize;
+ new_tcb->returnValueType = (returns_int) ? TcbThreadReturnValue::Integer : TcbThreadReturnValue::Pointer;
+ mlibc::sys_clone(new_tcb, &tid, stack);
+ *thread = reinterpret_cast<struct __mlibc_thread_data *>(new_tcb);
+
+ __atomic_store_n(&new_tcb->tid, tid, __ATOMIC_RELAXED);
+ mlibc::sys_futex_wake(&new_tcb->tid);
+
+ return 0;
+}
+
+int thread_join(struct __mlibc_thread_data *thread, void *ret) {
+ auto tcb = reinterpret_cast<Tcb *>(thread);
+
+ if (!__atomic_load_n(&tcb->isJoinable, __ATOMIC_ACQUIRE))
+ return EINVAL;
+
+ while (!__atomic_load_n(&tcb->didExit, __ATOMIC_ACQUIRE)) {
+ mlibc::sys_futex_wait(&tcb->didExit, 0, nullptr);
+ }
+
+ if(ret && tcb->returnValueType == TcbThreadReturnValue::Pointer)
+ *reinterpret_cast<void **>(ret) = tcb->returnValue.voidPtr;
+ else if(ret && tcb->returnValueType == TcbThreadReturnValue::Integer)
+ *reinterpret_cast<int *>(ret) = tcb->returnValue.intVal;
+
+ // FIXME: destroy tcb here, currently we leak it
+
+ return 0;
+}
+
+static constexpr size_t default_stacksize = 0x200000;
+static constexpr size_t default_guardsize = 4096;
+
+int thread_attr_init(struct __mlibc_threadattr *attr) {
+ *attr = __mlibc_threadattr{};
+ attr->__mlibc_stacksize = default_stacksize;
+ attr->__mlibc_guardsize = default_guardsize;
+ attr->__mlibc_detachstate = __MLIBC_THREAD_CREATE_JOINABLE;
+ return 0;
+}
+
+static constexpr unsigned int mutexRecursive = 1;
+static constexpr unsigned int mutexErrorCheck = 2;
+
+// TODO: either use uint32_t or determine the bit based on sizeof(int).
+static constexpr unsigned int mutex_owner_mask = (static_cast<uint32_t>(1) << 30) - 1;
+static constexpr unsigned int mutex_waiters_bit = static_cast<uint32_t>(1) << 31;
+
+// Only valid for the internal __mlibc_m mutex of wrlocks.
+static constexpr unsigned int mutex_excl_bit = static_cast<uint32_t>(1) << 30;
+
+int thread_mutex_init(struct __mlibc_mutex *__restrict mutex,
+ const struct __mlibc_mutexattr *__restrict attr) {
+ auto type = attr ? attr->__mlibc_type : __MLIBC_THREAD_MUTEX_DEFAULT;
+ auto robust = attr ? attr->__mlibc_robust : __MLIBC_THREAD_MUTEX_STALLED;
+ auto protocol = attr ? attr->__mlibc_protocol : __MLIBC_THREAD_PRIO_NONE;
+ auto pshared = attr ? attr->__mlibc_pshared : __MLIBC_THREAD_PROCESS_PRIVATE;
+
+ mutex->__mlibc_state = 0;
+ mutex->__mlibc_recursion = 0;
+ mutex->__mlibc_flags = 0;
+ mutex->__mlibc_prioceiling = 0; // TODO: We don't implement this.
+
+ if(type == __MLIBC_THREAD_MUTEX_RECURSIVE) {
+ mutex->__mlibc_flags |= mutexRecursive;
+ }else if(type == __MLIBC_THREAD_MUTEX_ERRORCHECK) {
+ mutex->__mlibc_flags |= mutexErrorCheck;
+ }else{
+ __ensure(type == __MLIBC_THREAD_MUTEX_NORMAL);
+ }
+
+ // TODO: Other values aren't supported yet.
+ __ensure(robust == __MLIBC_THREAD_MUTEX_STALLED);
+ __ensure(protocol == __MLIBC_THREAD_PRIO_NONE);
+ __ensure(pshared == __MLIBC_THREAD_PROCESS_PRIVATE);
+
+ return 0;
+}
+
+int thread_mutex_destroy(struct __mlibc_mutex *mutex) {
+ __ensure(!mutex->__mlibc_state);
+ return 0;
+}
+
+int thread_mutex_lock(struct __mlibc_mutex *mutex) {
+ unsigned int this_tid = mlibc::this_tid();
+ unsigned int expected = 0;
+ while(true) {
+ if(!expected) {
+ // Try to take the mutex here.
+ if(__atomic_compare_exchange_n(&mutex->__mlibc_state,
+ &expected, this_tid, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) {
+ __ensure(!mutex->__mlibc_recursion);
+ mutex->__mlibc_recursion = 1;
+ return 0;
+ }
+ }else{
+ // If this (recursive) mutex is already owned by us, increment the recursion level.
+ if((expected & mutex_owner_mask) == this_tid) {
+ if(!(mutex->__mlibc_flags & mutexRecursive)) {
+ if (mutex->__mlibc_flags & mutexErrorCheck)
+ return EDEADLK;
+ else
+ mlibc::panicLogger() << "mlibc: pthread_mutex deadlock detected!"
+ << frg::endlog;
+ }
+ ++mutex->__mlibc_recursion;
+ return 0;
+ }
+
+ // Wait on the futex if the waiters flag is set.
+ if(expected & mutex_waiters_bit) {
+ int e = mlibc::sys_futex_wait((int *)&mutex->__mlibc_state, expected, nullptr);
+
+ // If the wait returns EAGAIN, that means that the mutex_waiters_bit was just unset by
+ // some other thread. In this case, we should loop back around.
+ if (e && e != EAGAIN)
+ mlibc::panicLogger() << "sys_futex_wait() failed with error code " << e << frg::endlog;
+
+ // Opportunistically try to take the lock after we wake up.
+ expected = 0;
+ }else{
+ // Otherwise we have to set the waiters flag first.
+ unsigned int desired = expected | mutex_waiters_bit;
+ if(__atomic_compare_exchange_n((int *)&mutex->__mlibc_state,
+ reinterpret_cast<int*>(&expected), desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED))
+ expected = desired;
+ }
+ }
+ }
+}
+
+int thread_mutex_unlock(struct __mlibc_mutex *mutex) {
+ // Decrement the recursion level and unlock if we hit zero.
+ __ensure(mutex->__mlibc_recursion);
+ if(--mutex->__mlibc_recursion)
+ return 0;
+
+ auto flags = mutex->__mlibc_flags;
+
+ // Reset the mutex to the unlocked state.
+ auto state = __atomic_exchange_n(&mutex->__mlibc_state, 0, __ATOMIC_RELEASE);
+
+ // After this point the mutex is unlocked, and therefore we cannot access its contents as it
+ // may have been destroyed by another thread.
+
+ unsigned int this_tid = mlibc::this_tid();
+ if ((flags & mutexErrorCheck) && (state & mutex_owner_mask) != this_tid)
+ return EPERM;
+
+ if ((flags & mutexErrorCheck) && !(state & mutex_owner_mask))
+ return EINVAL;
+
+ __ensure((state & mutex_owner_mask) == this_tid);
+
+ if(state & mutex_waiters_bit) {
+ // Wake the futex if there were waiters. Since the mutex might not exist at this location
+ // anymore, we must conservatively ignore EACCES and EINVAL which may occur as a result.
+ int e = mlibc::sys_futex_wake((int *)&mutex->__mlibc_state);
+ __ensure(e >= 0 || e == EACCES || e == EINVAL);
+ }
+
+ return 0;
+}
+
+int thread_mutexattr_init(struct __mlibc_mutexattr *attr) {
+ attr->__mlibc_type = __MLIBC_THREAD_MUTEX_DEFAULT;
+ attr->__mlibc_robust = __MLIBC_THREAD_MUTEX_STALLED;
+ attr->__mlibc_pshared = __MLIBC_THREAD_PROCESS_PRIVATE;
+ attr->__mlibc_protocol = __MLIBC_THREAD_PRIO_NONE;
+ return 0;
+}
+
+int thread_mutexattr_destroy(struct __mlibc_mutexattr *attr) {
+ memset(attr, 0, sizeof(*attr));
+ return 0;
+}
+
+int thread_mutexattr_gettype(const struct __mlibc_mutexattr *__restrict attr, int *__restrict type) {
+ *type = attr->__mlibc_type;
+ return 0;
+}
+
+int thread_mutexattr_settype(struct __mlibc_mutexattr *attr, int type) {
+ if (type != __MLIBC_THREAD_MUTEX_NORMAL && type != __MLIBC_THREAD_MUTEX_ERRORCHECK
+ && type != __MLIBC_THREAD_MUTEX_RECURSIVE)
+ return EINVAL;
+
+ attr->__mlibc_type = type;
+ return 0;
+}
+
+int thread_cond_init(struct __mlibc_cond *__restrict cond, const struct __mlibc_condattr *__restrict attr) {
+ auto clock = attr ? attr->__mlibc_clock : CLOCK_REALTIME;
+ auto pshared = attr ? attr->__mlibc_pshared : __MLIBC_THREAD_PROCESS_PRIVATE;
+
+ cond->__mlibc_clock = clock;
+ cond->__mlibc_flags = pshared;
+
+ __atomic_store_n(&cond->__mlibc_seq, 1, __ATOMIC_RELAXED);
+
+ return 0;
+}
+
+int thread_cond_destroy(struct __mlibc_cond *) {
+ return 0;
+}
+
+int thread_cond_broadcast(struct __mlibc_cond *cond) {
+ __atomic_fetch_add(&cond->__mlibc_seq, 1, __ATOMIC_RELEASE);
+ if(int e = mlibc::sys_futex_wake((int *)&cond->__mlibc_seq); e)
+ __ensure(!"sys_futex_wake() failed");
+
+ return 0;
+}
+
+int thread_cond_timedwait(struct __mlibc_cond *__restrict cond, __mlibc_mutex *__restrict mutex,
+ const struct timespec *__restrict abstime) {
+ // TODO: pshared isn't supported yet.
+ __ensure(cond->__mlibc_flags == 0);
+
+ constexpr long nanos_per_second = 1'000'000'000;
+ if (abstime && (abstime->tv_nsec < 0 || abstime->tv_nsec >= nanos_per_second))
+ return EINVAL;
+
+ auto seq = __atomic_load_n(&cond->__mlibc_seq, __ATOMIC_ACQUIRE);
+
+ // TODO: handle locking errors and cancellation properly.
+ while (true) {
+ if (thread_mutex_unlock(mutex))
+ __ensure(!"Failed to unlock the mutex");
+
+ int e;
+ if (abstime) {
+ // Adjust for the fact that sys_futex_wait accepts a *timeout*, but
+ // pthread_cond_timedwait accepts an *absolute time*.
+ // Note: mlibc::sys_clock_get is available unconditionally.
+ struct timespec now;
+ if (mlibc::sys_clock_get(cond->__mlibc_clock, &now.tv_sec, &now.tv_nsec))
+ __ensure(!"sys_clock_get() failed");
+
+ struct timespec timeout;
+ timeout.tv_sec = abstime->tv_sec - now.tv_sec;
+ timeout.tv_nsec = abstime->tv_nsec - now.tv_nsec;
+
+ // Check if abstime has already passed.
+ if (timeout.tv_sec < 0 || (timeout.tv_sec == 0 && timeout.tv_nsec < 0)) {
+ if (thread_mutex_lock(mutex))
+ __ensure(!"Failed to lock the mutex");
+ return ETIMEDOUT;
+ } else if (timeout.tv_nsec >= nanos_per_second) {
+ timeout.tv_nsec -= nanos_per_second;
+ timeout.tv_sec++;
+ __ensure(timeout.tv_nsec < nanos_per_second);
+ } else if (timeout.tv_nsec < 0) {
+ timeout.tv_nsec += nanos_per_second;
+ timeout.tv_sec--;
+ __ensure(timeout.tv_nsec >= 0);
+ }
+
+ e = mlibc::sys_futex_wait((int *)&cond->__mlibc_seq, seq, &timeout);
+ } else {
+ e = mlibc::sys_futex_wait((int *)&cond->__mlibc_seq, seq, nullptr);
+ }
+
+ if (thread_mutex_lock(mutex))
+ __ensure(!"Failed to lock the mutex");
+
+ // There are four cases to handle:
+ // 1. e == 0: this indicates a (potentially spurious) wakeup. The value of
+ // seq *must* be checked to distinguish these two cases.
+ // 2. e == EAGAIN: this indicates that the value of seq changed before we
+ // went to sleep. We don't need to check seq in this case.
+ // 3. e == EINTR: a signal was delivered. The man page allows us to choose
+ // whether to go to sleep again or to return 0, but we do the former
+ // to match other libcs.
+ // 4. e == ETIMEDOUT: this should only happen if abstime is set.
+ if (e == 0) {
+ auto cur_seq = __atomic_load_n(&cond->__mlibc_seq, __ATOMIC_ACQUIRE);
+ if (cur_seq > seq)
+ return 0;
+ } else if (e == EAGAIN) {
+ __ensure(__atomic_load_n(&cond->__mlibc_seq, __ATOMIC_ACQUIRE) > seq);
+ return 0;
+ } else if (e == EINTR) {
+ continue;
+ } else if (e == ETIMEDOUT) {
+ __ensure(abstime);
+ return ETIMEDOUT;
+ } else {
+ mlibc::panicLogger() << "sys_futex_wait() failed with error " << e << frg::endlog;
+ }
+ }
+}
+
+} // namespace mlibc
diff --git a/lib/mlibc/options/internal/generic/ubsan.cpp b/lib/mlibc/options/internal/generic/ubsan.cpp
new file mode 100644
index 0000000..3491729
--- /dev/null
+++ b/lib/mlibc/options/internal/generic/ubsan.cpp
@@ -0,0 +1,254 @@
+#include <limits.h>
+#include <mlibc/debug.hpp>
+
+#define FMT(obj) format_object((obj), opts, formatter)
+
+#define LOG_NAME_LOC(name, loc) "ubsan: " name " at " << loc << "\n "
+#define LOG_LHS_RHS(lhs, rhs) "LHS = " << (lhs) << ", RHS = " << (rhs)
+
+struct SourceLocation {
+ const char *filename;
+ uint32_t line;
+ uint32_t column;
+};
+
+template<class F>
+void format_object(const SourceLocation &loc, frg::format_options opts, F &formatter) {
+ FMT(loc.filename);
+ FMT(":");
+ FMT(loc.line);
+ FMT(":");
+ FMT(loc.column);
+}
+
+using ValueHandle = uintptr_t;
+
+struct TypeDescriptor {
+ enum class Kind : uint16_t {
+ Integer = 0x0000,
+ Float = 0x0001,
+ Unknown = 0xffff
+ } kind;
+
+ uint16_t info;
+ char name[];
+
+ unsigned bitWidthInt() const {
+ return 1 << (info >> 1);
+ }
+
+ bool isInlineInt() const {
+ if (kind != Kind::Integer)
+ return false;
+
+ auto inlineBits = sizeof(ValueHandle) * CHAR_BIT;
+ auto valueBits = bitWidthInt();
+ return inlineBits <= valueBits;
+ }
+
+ bool isSigned() const {
+ return info & 1;
+ }
+};
+
+template<class F>
+void format_object(const TypeDescriptor &type, frg::format_options opts, F &formatter) {
+ FMT(type.name);
+}
+
+struct Value {
+ const TypeDescriptor &type;
+ ValueHandle val;
+
+ Value(const TypeDescriptor &type, ValueHandle val) : type(type), val(val) {}
+};
+
+template<class F>
+void format_object(const Value &val, frg::format_options opts, F &formatter) {
+ if (val.type.isInlineInt() && val.type.isSigned()) {
+ auto signedValue = static_cast<int64_t>(val.val);
+ FMT(signedValue);
+ } else if (val.type.isInlineInt() && !val.type.isSigned()) {
+ auto unsignedValue = static_cast<uint64_t>(val.val);
+ FMT(unsignedValue);
+ }
+
+ FMT(" (");
+ FMT(val.type);
+ FMT(")");
+}
+
+
+// --- Hook implementations ---
+
+struct TypeMismatch {
+ SourceLocation loc;
+ const TypeDescriptor &type;
+ unsigned char logAlignment;
+ unsigned char kind;
+};
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_type_mismatch_v1(TypeMismatch *tm, ValueHandle pointer) {
+ // TODO: Make this print more information.
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("type mismatch", tm->loc)
+ << "accessed address " << (void *)pointer << " but type "
+ << tm->type << " requires alignment " << (1 << tm->logAlignment)
+ << frg::endlog;
+}
+
+struct PointerOverflowData {
+ SourceLocation loc;
+};
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_pointer_overflow(PointerOverflowData *pod, ValueHandle base, ValueHandle result) {
+ (void)base;
+ (void)result;
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("pointer overflow", pod->loc)
+ << frg::endlog;
+}
+
+struct InvalidValueData {
+ SourceLocation loc;
+ const TypeDescriptor &type;
+};
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_load_invalid_value(InvalidValueData *ivd, ValueHandle value) {
+ (void)value;
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("load of invalid value", ivd->loc)
+ << frg::endlog;
+}
+
+struct OverflowData {
+ SourceLocation loc;
+ const TypeDescriptor &type;
+};
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_add_overflow(OverflowData *od, ValueHandle lhs, ValueHandle rhs) {
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("add overflowed ", od->loc)
+ << LOG_LHS_RHS(Value(od->type, lhs), Value(od->type, rhs))
+ << frg::endlog;
+}
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_sub_overflow(OverflowData *od, ValueHandle lhs, ValueHandle rhs) {
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("sub overflowed", od->loc)
+ << LOG_LHS_RHS(Value(od->type, lhs), Value(od->type, rhs))
+ << frg::endlog;
+}
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_mul_overflow(OverflowData *od, ValueHandle lhs, ValueHandle rhs) {
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("mul overflowed", od->loc)
+ << LOG_LHS_RHS(Value(od->type, lhs), Value(od->type, rhs))
+ << frg::endlog;
+}
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_divrem_overflow(OverflowData *od, ValueHandle lhs, ValueHandle rhs) {
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("divrem overflowed", od->loc)
+ << LOG_LHS_RHS(Value(od->type, lhs), Value(od->type, rhs))
+ << frg::endlog;
+}
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_negate_overflow(OverflowData *od, ValueHandle lhs, ValueHandle rhs) {
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("negate overflowed", od->loc)
+ << LOG_LHS_RHS(Value(od->type, lhs), Value(od->type, rhs))
+ << frg::endlog;
+}
+
+struct ShiftOutOfBoundsData {
+ SourceLocation loc;
+ const TypeDescriptor &lhsType;
+ const TypeDescriptor &rhsType;
+};
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *soob, ValueHandle lhs, ValueHandle rhs) {
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("shift out of bounds", soob->loc)
+ << LOG_LHS_RHS(Value(soob->lhsType, lhs), Value(soob->rhsType, rhs))
+ << frg::endlog;
+}
+
+struct OutOfBoundsData {
+ SourceLocation loc;
+ const TypeDescriptor &arrayType;
+ const TypeDescriptor &indexType;
+};
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_out_of_bounds(OutOfBoundsData *oobd, ValueHandle data) {
+ (void)data;
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("out of bounds access", oobd->loc)
+ << frg::endlog;
+}
+
+struct UnreachableData {
+ SourceLocation loc;
+};
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_builtin_unreachable(UnreachableData *ubd) {
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("reached __builtin_unreachable()", ubd->loc)
+ << frg::endlog;
+}
+
+struct InvalidBuiltinData {
+ SourceLocation loc;
+ unsigned char kind;
+};
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_invalid_builtin(InvalidBuiltinData *ibd) {
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("reached invalid builtin", ibd->loc)
+ << frg::endlog;
+}
+
+struct VLABoundData {
+ SourceLocation loc;
+ const TypeDescriptor &type;
+};
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_vla_bound_not_positive(VLABoundData *vlabd) {
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("VLA bound not positive", vlabd->loc)
+ << frg::endlog;
+}
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_missing_return(UnreachableData *data) {
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("reached end of a value-returning function without returning a value", data->loc)
+ << frg::endlog;
+}
+
+struct NonNullArgData {
+ SourceLocation loc;
+ SourceLocation attr_loc;
+ int arg_index;
+};
+
+extern "C" [[gnu::visibility("hidden")]]
+void __ubsan_handle_nonnull_arg(NonNullArgData *data) {
+ mlibc::panicLogger()
+ << LOG_NAME_LOC("null pointer passed to non-null argument", data->loc)
+ << "argument " << data->arg_index << " is required to be non-null in "
+ << data->attr_loc << frg::endlog;
+}
diff --git a/lib/mlibc/options/internal/include/bits/cpu_set.h b/lib/mlibc/options/internal/include/bits/cpu_set.h
new file mode 100644
index 0000000..69f6923
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/cpu_set.h
@@ -0,0 +1,13 @@
+#ifndef _MLIBC_INTERNAL_CPU_SET_H
+#define _MLIBC_INTERNAL_CPU_SET_H
+
+typedef unsigned long __cpu_mask;
+
+#define CPU_SETSIZE 1024
+#define __NCPUBITS (8 * sizeof(__cpu_mask))
+
+typedef struct {
+ __cpu_mask __bits[CPU_SETSIZE / __NCPUBITS];
+} cpu_set_t;
+
+#endif /* _MLIBC_INTERNAL_CPU_SET_H */
diff --git a/lib/mlibc/options/internal/include/bits/ensure.h b/lib/mlibc/options/internal/include/bits/ensure.h
new file mode 100644
index 0000000..f75a2e9
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/ensure.h
@@ -0,0 +1,45 @@
+
+#ifndef MLIBC_ENSURE_H
+#define MLIBC_ENSURE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+void __ensure_fail(const char *assertion, const char *file, unsigned int line,
+ const char *function);
+
+void __ensure_warn(const char *assertion, const char *file, unsigned int line,
+ const char *function);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#define __ensure(assertion) do { if(!(assertion)) \
+ __ensure_fail(#assertion, __FILE__, __LINE__, __func__); } while(0)
+
+#define MLIBC_UNIMPLEMENTED() __ensure_fail("Functionality is not implemented", \
+ __FILE__, __LINE__, __func__)
+
+#define MLIBC_MISSING_SYSDEP() __ensure_warn("Library function fails due to missing sysdep", \
+ __FILE__, __LINE__, __func__)
+
+#define MLIBC_CHECK_OR_ENOSYS(sysdep, ret) ({ \
+ if (!(sysdep)) { \
+ __ensure_warn("Library function fails due to missing sysdep", \
+ __FILE__, __LINE__, __func__); \
+ errno = ENOSYS; \
+ return (ret); \
+ } \
+ sysdep; \
+ })
+
+#define MLIBC_STUB_BODY { MLIBC_UNIMPLEMENTED(); __builtin_unreachable(); }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MLIBC_ENSURE_H
+
diff --git a/lib/mlibc/options/internal/include/bits/ether_addr.h b/lib/mlibc/options/internal/include/bits/ether_addr.h
new file mode 100644
index 0000000..1631e98
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/ether_addr.h
@@ -0,0 +1,10 @@
+#ifndef MLIBC_ETHER_ADDR_H
+#define MLIBC_ETHER_ADDR_H
+
+#include <stdint.h>
+
+struct ether_addr {
+ uint8_t ether_addr_octet[6];
+} __attribute__((__packed__));
+
+#endif // MLIBC_ETHER_ADDR_H
diff --git a/lib/mlibc/options/internal/include/bits/inline-definition.h b/lib/mlibc/options/internal/include/bits/inline-definition.h
new file mode 100644
index 0000000..ec4c4da
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/inline-definition.h
@@ -0,0 +1,19 @@
+#ifndef MLIBC_INLINE_DEFINITION_H
+#define MLIBC_INLINE_DEFINITION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __MLIBC_EMIT_INLINE_DEFINITIONS
+#define __MLIBC_INLINE_DEFINITION
+#else
+#define __MLIBC_INLINE_DEFINITION __attribute__((__gnu_inline__)) extern __inline__
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MLIBC_INLINE_DEFINITION_H
+
diff --git a/lib/mlibc/options/internal/include/bits/machine.h b/lib/mlibc/options/internal/include/bits/machine.h
new file mode 100644
index 0000000..371a94b
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/machine.h
@@ -0,0 +1,86 @@
+
+#ifndef MLIBC_MACHINE_H
+#define MLIBC_MACHINE_H
+
+#include <stdint.h>
+
+#if defined (__i386__)
+struct __mlibc_jmpbuf_register_state {
+ uint32_t ebx;
+ uint32_t ebp;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t esp;
+ uint32_t eip;
+};
+#elif defined (__x86_64__)
+struct __mlibc_jmpbuf_register_state {
+ uint64_t rbx;
+ uint64_t rbp;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rsp;
+ uint64_t rip;
+};
+#elif defined (__aarch64__)
+struct __mlibc_jmpbuf_register_state {
+ uint64_t x19;
+ uint64_t x20;
+ uint64_t x21;
+ uint64_t x22;
+ uint64_t x23;
+ uint64_t x24;
+ uint64_t x25;
+ uint64_t x26;
+ uint64_t x27;
+ uint64_t x28;
+ uint64_t x29;
+ uint64_t x30;
+ uint64_t sp;
+ uint64_t pad;
+ uint64_t d8;
+ uint64_t d9;
+ uint64_t d10;
+ uint64_t d11;
+ uint64_t d12;
+ uint64_t d13;
+ uint64_t d14;
+ uint64_t d15;
+};
+#elif defined (__riscv) && __riscv_xlen == 64
+struct __mlibc_jmpbuf_register_state {
+ uint64_t ra;
+ uint64_t s0;
+ uint64_t s1;
+ uint64_t s2;
+ uint64_t s3;
+ uint64_t s4;
+ uint64_t s5;
+ uint64_t s6;
+ uint64_t s7;
+ uint64_t s8;
+ uint64_t s9;
+ uint64_t s10;
+ uint64_t s11;
+ uint64_t sp;
+ double fs0;
+ double fs1;
+ double fs2;
+ double fs3;
+ double fs4;
+ double fs5;
+ double fs6;
+ double fs7;
+ double fs8;
+ double fs9;
+ double fs10;
+ double fs11;
+};
+#else
+# error "Missing architecture specific code"
+#endif
+
+#endif // MLIBC_MACHINE_H
+
diff --git a/lib/mlibc/options/internal/include/bits/mbstate.h b/lib/mlibc/options/internal/include/bits/mbstate.h
new file mode 100644
index 0000000..2bfb3eb
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/mbstate.h
@@ -0,0 +1,12 @@
+#ifndef MLIBC_MBSTATE_H
+#define MLIBC_MBSTATE_H
+
+struct __mlibc_mbstate {
+ short __progress;
+ short __shift;
+ unsigned int __cpoint;
+};
+
+#define __MLIBC_MBSTATE_INITIALIZER {0, 0, 0}
+
+#endif // MLIBC_MBSTATE_H
diff --git a/lib/mlibc/options/internal/include/bits/nl_item.h b/lib/mlibc/options/internal/include/bits/nl_item.h
new file mode 100644
index 0000000..dc882dc
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/nl_item.h
@@ -0,0 +1,82 @@
+
+#ifndef _NL_ITEM_H
+#define _NL_ITEM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int nl_item;
+
+#define ABDAY_1 0x60000
+#define ABDAY_2 0x60001
+#define ABDAY_3 0x60002
+#define ABDAY_4 0x60003
+#define ABDAY_5 0x60004
+#define ABDAY_6 0x60005
+#define ABDAY_7 0x60006
+
+#define DAY_1 0x60007
+#define DAY_2 0x60008
+#define DAY_3 0x60009
+#define DAY_4 0x6000A
+#define DAY_5 0x6000B
+#define DAY_6 0x6000C
+#define DAY_7 0x6000D
+
+#define ABMON_1 0x6000E
+#define ABMON_2 0x6000F
+#define ABMON_3 0x60010
+#define ABMON_4 0x60011
+#define ABMON_5 0x60012
+#define ABMON_6 0x60013
+#define ABMON_7 0x60014
+#define ABMON_8 0x60015
+#define ABMON_9 0x60016
+#define ABMON_10 0x60017
+#define ABMON_11 0x60018
+#define ABMON_12 0x60019
+
+#define MON_1 0x6001A
+#define MON_2 0x6001B
+#define MON_3 0x6001C
+#define MON_4 0x6001D
+#define MON_5 0x6001E
+#define MON_6 0x6001F
+#define MON_7 0x60020
+#define MON_8 0x60021
+#define MON_9 0x60022
+#define MON_10 0x60023
+#define MON_11 0x60024
+#define MON_12 0x60025
+
+#define AM_STR 0x60026
+#define PM_STR 0x60027
+
+#define D_T_FMT 0x60028
+#define D_FMT 0x60029
+#define T_FMT 0x6002A
+#define T_FMT_AMPM 0x6002B
+
+#define ERA 0x6002C
+#define ERA_D_FMT 0x6002D
+#define ALT_DIGITS 0x6002E
+#define ERA_D_T_FMT 0x6002F
+#define ERA_T_FMT 0x60030
+
+#define CODESET 0x30000
+
+#define CRNCYSTR 0x40000
+
+#define RADIXCHAR 0x50000
+#define THOUSEP 0x50001
+
+#define YESEXPR 0x70000
+#define NOEXPR 0x70001
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _NL_ITEM_H
+
diff --git a/lib/mlibc/options/internal/include/bits/null.h b/lib/mlibc/options/internal/include/bits/null.h
new file mode 100644
index 0000000..7d3fa7b
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/null.h
@@ -0,0 +1,16 @@
+
+#ifndef MLIBC_NULL_H
+#define MLIBC_NULL_H
+
+#ifdef NULL
+#undef NULL
+#endif
+
+#ifndef __cplusplus
+# define NULL ((void *)0)
+#else
+# define NULL 0
+#endif
+
+#endif // MLIBC_NULL_H
+
diff --git a/lib/mlibc/options/internal/include/bits/off_t.h b/lib/mlibc/options/internal/include/bits/off_t.h
new file mode 100644
index 0000000..929fae9
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/off_t.h
@@ -0,0 +1,8 @@
+#ifndef MLIBC_OFF_T_H
+#define MLIBC_OFF_T_H
+
+// TODO: use something like int64_t instead?
+typedef long off_t;
+typedef long off64_t;
+
+#endif // MLIBC_OFF_T_H
diff --git a/lib/mlibc/options/internal/include/bits/sigset_t.h b/lib/mlibc/options/internal/include/bits/sigset_t.h
new file mode 100644
index 0000000..bb86848
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/sigset_t.h
@@ -0,0 +1,25 @@
+#ifndef MLIBC_BITS_SIGSET_T_H
+#define MLIBC_BITS_SIGSET_T_H
+
+#include <abi-bits/signal.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+// functions to manage sigset_t
+int sigemptyset(sigset_t *);
+int sigfillset(sigset_t *);
+int sigaddset(sigset_t *, int);
+int sigdelset(sigset_t *, int);
+int sigismember(const sigset_t *set, int sig);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //MLIBC_BITS_SIGSET_T_H
diff --git a/lib/mlibc/options/internal/include/bits/size_t.h b/lib/mlibc/options/internal/include/bits/size_t.h
new file mode 100644
index 0000000..46d7486
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/size_t.h
@@ -0,0 +1,6 @@
+#ifndef MLIBC_SIZE_T_H
+#define MLIBC_SIZE_T_H
+
+typedef __SIZE_TYPE__ size_t;
+
+#endif // MLIBC_SIZE_T_H
diff --git a/lib/mlibc/options/internal/include/bits/ssize_t.h b/lib/mlibc/options/internal/include/bits/ssize_t.h
new file mode 100644
index 0000000..c450233
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/ssize_t.h
@@ -0,0 +1,15 @@
+
+#ifndef MLIBC_SSIZE_T_H
+#define MLIBC_SSIZE_T_H
+
+// TODO: use ptrdiff_t instead?
+#if __UINTPTR_MAX__ == __UINT64_MAX__
+typedef long ssize_t;
+#elif __UINTPTR_MAX__ == __UINT32_MAX__
+typedef int ssize_t;
+#else
+#error "unsupported architecture"
+#endif
+
+#endif // MLIBC_SSIZE_T_H
+
diff --git a/lib/mlibc/options/internal/include/bits/threads.h b/lib/mlibc/options/internal/include/bits/threads.h
new file mode 100644
index 0000000..3feb4c3
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/threads.h
@@ -0,0 +1,79 @@
+#ifndef _INTERNAL_THREADS_H
+#define _INTERNAL_THREADS_H
+
+#include <abi-bits/clockid_t.h>
+#include <bits/size_t.h>
+#include <bits/cpu_set.h>
+#include <bits/sigset_t.h>
+
+// values for pthread_attr_{get,set}detachstate().
+#define __MLIBC_THREAD_CREATE_JOINABLE 0
+#define __MLIBC_THREAD_CREATE_DETACHED 1
+
+// values for pthread_mutexattr_{get,set}type().
+#define __MLIBC_THREAD_MUTEX_DEFAULT 0
+#define __MLIBC_THREAD_MUTEX_NORMAL 0
+#define __MLIBC_THREAD_MUTEX_ERRORCHECK 1
+#define __MLIBC_THREAD_MUTEX_RECURSIVE 2
+
+// values for pthread_mutexattr_{get,set}pshared().
+#define __MLIBC_THREAD_PROCESS_PRIVATE 0
+#define __MLIBC_THREAD_PROCESS_SHARED 1
+
+// values for pthread_mutexattr_{get,set}robust().
+#define __MLIBC_THREAD_MUTEX_STALLED 0
+#define __MLIBC_THREAD_MUTEX_ROBUST 1
+
+// Values for pthread_mutexattr_{get,set}protocol()
+#define __MLIBC_THREAD_PRIO_NONE 0
+#define __MLIBC_THREAD_PRIO_INHERIT 1
+#define __MLIBC_THREAD_PRIO_PROTECT 2
+
+struct sched_param {
+ int sched_priority;
+};
+
+struct __mlibc_thread_data;
+
+struct __mlibc_threadattr {
+ size_t __mlibc_guardsize;
+ size_t __mlibc_stacksize;
+ void *__mlibc_stackaddr;
+ int __mlibc_detachstate;
+ int __mlibc_scope;
+ int __mlibc_inheritsched;
+ struct sched_param __mlibc_schedparam;
+ int __mlibc_schedpolicy;
+ cpu_set_t *__mlibc_cpuset;
+ size_t __mlibc_cpusetsize;
+ sigset_t __mlibc_sigmask;
+ int __mlibc_sigmaskset;
+};
+
+struct __mlibc_mutex {
+ unsigned int __mlibc_state;
+ unsigned int __mlibc_recursion;
+ unsigned int __mlibc_flags;
+ int __mlibc_prioceiling;
+};
+
+struct __mlibc_mutexattr {
+ int __mlibc_type;
+ int __mlibc_robust;
+ int __mlibc_protocol;
+ int __mlibc_pshared;
+ int __mlibc_prioceiling;
+};
+
+struct __mlibc_cond {
+ unsigned int __mlibc_seq;
+ unsigned int __mlibc_flags;
+ clockid_t __mlibc_clock;
+};
+
+struct __mlibc_condattr {
+ int __mlibc_pshared;
+ clockid_t __mlibc_clock;
+};
+
+#endif /* _INTERNAL_THREADS_H */
diff --git a/lib/mlibc/options/internal/include/bits/types.h b/lib/mlibc/options/internal/include/bits/types.h
new file mode 100644
index 0000000..935c5e0
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/types.h
@@ -0,0 +1,319 @@
+#ifndef _MLIBC_INTERNAL_TYPES_H
+#define _MLIBC_INTERNAL_TYPES_H
+
+typedef __UINT8_TYPE__ __mlibc_uint8;
+typedef __UINT16_TYPE__ __mlibc_uint16;
+typedef __UINT32_TYPE__ __mlibc_uint32;
+typedef __UINT64_TYPE__ __mlibc_uint64;
+
+typedef __INT8_TYPE__ __mlibc_int8;
+typedef __INT16_TYPE__ __mlibc_int16;
+typedef __INT32_TYPE__ __mlibc_int32;
+typedef __INT64_TYPE__ __mlibc_int64;
+
+// Clang and GCC have different mechanisms for INT32_C and friends.
+#ifdef __clang__
+# define __MLIBC_C_EXPAND_JOIN(x, suffix) x ## suffix
+# define __MLIBC_C_JOIN(x, suffix) __MLIBC_C_EXPAND_JOIN(x, suffix)
+
+# define __MLIBC_INT8_C(x) __MLIBC_C_JOIN(x, __INT8_C_SUFFIX__)
+# define __MLIBC_INT16_C(x) __MLIBC_C_JOIN(x, __INT16_C_SUFFIX__)
+# define __MLIBC_INT32_C(x) __MLIBC_C_JOIN(x, __INT32_C_SUFFIX__)
+# define __MLIBC_INT64_C(x) __MLIBC_C_JOIN(x, __INT64_C_SUFFIX__)
+
+# define __MLIBC_UINT8_C(x) __MLIBC_C_JOIN(x, __UINT8_C_SUFFIX__)
+# define __MLIBC_UINT16_C(x) __MLIBC_C_JOIN(x, __UINT16_C_SUFFIX__)
+# define __MLIBC_UINT32_C(x) __MLIBC_C_JOIN(x, __UINT32_C_SUFFIX__)
+# define __MLIBC_UINT64_C(x) __MLIBC_C_JOIN(x, __UINT64_C_SUFFIX__)
+
+# define __MLIBC_INTMAX_C(x) __MLIBC_C_JOIN(x, __INTMAX_C_SUFFIX__)
+# define __MLIBC_UINTMAX_C(x) __MLIBC_C_JOIN(x, __UINTMAX_C_SUFFIX__)
+#else
+# define __MLIBC_INT8_C(x) __INT8_C(x)
+# define __MLIBC_INT16_C(x) __INT16_C(x)
+# define __MLIBC_INT32_C(x) __INT32_C(x)
+# define __MLIBC_INT64_C(x) __INT64_C(x)
+
+# define __MLIBC_UINT8_C(x) __UINT8_C(x)
+# define __MLIBC_UINT16_C(x) __UINT16_C(x)
+# define __MLIBC_UINT32_C(x) __UINT32_C(x)
+# define __MLIBC_UINT64_C(x) __UINT64_C(x)
+
+# define __MLIBC_INTMAX_C(x) __INTMAX_C(x)
+# define __MLIBC_UINTMAX_C(x) __UINTMAX_C(x)
+#endif
+
+#define __MLIBC_INT8_MAX __INT8_MAX__
+#define __MLIBC_INT16_MAX __INT16_MAX__
+#define __MLIBC_INT32_MAX __INT32_MAX__
+#define __MLIBC_INT64_MAX __INT64_MAX__
+
+#define __MLIBC_INT8_MIN (-__MLIBC_INT8_MAX - 1)
+#define __MLIBC_INT16_MIN (-__MLIBC_INT16_MAX - 1)
+#define __MLIBC_INT32_MIN (-__MLIBC_INT32_MAX - 1)
+#define __MLIBC_INT64_MIN (-__MLIBC_INT64_MAX - 1)
+
+#define __MLIBC_UINT8_MAX __UINT8_MAX__
+#define __MLIBC_UINT16_MAX __UINT16_MAX__
+#define __MLIBC_UINT32_MAX __UINT32_MAX__
+#define __MLIBC_UINT64_MAX __UINT64_MAX__
+
+// Fast types (signed).
+
+#if defined (__i386__)
+
+typedef __mlibc_int8 __mlibc_int_fast8;
+#define __MLIBC_INT_FAST8_C(x) __MLIBC_INT8_C(x)
+#define __MLIBC_INT_FAST8_MAX __MLIBC_INT8_MAX
+#define __MLIBC_INT_FAST8_MIN __MLIBC_INT8_MIN
+
+typedef __mlibc_int32 __mlibc_int_fast16;
+#define __MLIBC_INT_FAST16_C(x) __MLIBC_INT32_C(x)
+#define __MLIBC_INT_FAST16_MAX __MLIBC_INT32_MAX
+#define __MLIBC_INT_FAST16_MIN __MLIBC_INT32_MIN
+
+typedef __mlibc_int32 __mlibc_int_fast32;
+#define __MLIBC_INT_FAST32_C(x) __MLIBC_INT32_C(x)
+#define __MLIBC_INT_FAST32_MAX __MLIBC_INT32_MAX
+#define __MLIBC_INT_FAST32_MIN __MLIBC_INT32_MIN
+
+typedef __mlibc_int64 __mlibc_int_fast64;
+#define __MLIBC_INT_FAST64_C(x) __MLIBC_INT64_C(x)
+#define __MLIBC_INT_FAST64_MAX __MLIBC_INT64_MAX
+#define __MLIBC_INT_FAST64_MIN __MLIBC_INT64_MIN
+
+#elif defined (__x86_64__)
+
+typedef __mlibc_int8 __mlibc_int_fast8;
+#define __MLIBC_INT_FAST8_C(x) __MLIBC_INT8_C(x)
+#define __MLIBC_INT_FAST8_MAX __MLIBC_INT8_MAX
+#define __MLIBC_INT_FAST8_MIN __MLIBC_INT8_MIN
+
+typedef __mlibc_int64 __mlibc_int_fast16;
+#define __MLIBC_INT_FAST16_C(x) __MLIBC_INT64_C(x)
+#define __MLIBC_INT_FAST16_MAX __MLIBC_INT64_MAX
+#define __MLIBC_INT_FAST16_MIN __MLIBC_INT64_MIN
+
+typedef __mlibc_int64 __mlibc_int_fast32;
+#define __MLIBC_INT_FAST32_C(x) __MLIBC_INT64_C(x)
+#define __MLIBC_INT_FAST32_MAX __MLIBC_INT64_MAX
+#define __MLIBC_INT_FAST32_MIN __MLIBC_INT64_MIN
+
+typedef __mlibc_int64 __mlibc_int_fast64;
+#define __MLIBC_INT_FAST64_C(x) __MLIBC_INT64_C(x)
+#define __MLIBC_INT_FAST64_MAX __MLIBC_INT64_MAX
+#define __MLIBC_INT_FAST64_MIN __MLIBC_INT64_MIN
+
+#elif defined (__aarch64__)
+
+typedef __mlibc_int8 __mlibc_int_fast8;
+#define __MLIBC_INT_FAST8_C(x) __MLIBC_INT8_C(x)
+#define __MLIBC_INT_FAST8_MAX __MLIBC_INT8_MAX
+#define __MLIBC_INT_FAST8_MIN __MLIBC_INT8_MIN
+
+typedef __mlibc_int64 __mlibc_int_fast16;
+#define __MLIBC_INT_FAST16_C(x) __MLIBC_INT64_C(x)
+#define __MLIBC_INT_FAST16_MAX __MLIBC_INT64_MAX
+#define __MLIBC_INT_FAST16_MIN __MLIBC_INT64_MIN
+
+typedef __mlibc_int64 __mlibc_int_fast32;
+#define __MLIBC_INT_FAST32_C(x) __MLIBC_INT64_C(x)
+#define __MLIBC_INT_FAST32_MAX __MLIBC_INT64_MAX
+#define __MLIBC_INT_FAST32_MIN __MLIBC_INT64_MIN
+
+typedef __mlibc_int64 __mlibc_int_fast64;
+#define __MLIBC_INT_FAST64_C(x) __MLIBC_INT64_C(x)
+#define __MLIBC_INT_FAST64_MAX __MLIBC_INT64_MAX
+#define __MLIBC_INT_FAST64_MIN __MLIBC_INT64_MIN
+
+#elif defined (__riscv) && __riscv_xlen == 64
+
+typedef __mlibc_int8 __mlibc_int_fast8;
+#define __MLIBC_INT_FAST8_C(x) __MLIBC_INT8_C(x)
+#define __MLIBC_INT_FAST8_MAX __MLIBC_INT8_MAX
+#define __MLIBC_INT_FAST8_MIN __MLIBC_INT8_MIN
+
+typedef __mlibc_int64 __mlibc_int_fast16;
+#define __MLIBC_INT_FAST16_C(x) __MLIBC_INT64_C(x)
+#define __MLIBC_INT_FAST16_MAX __MLIBC_INT64_MAX
+#define __MLIBC_INT_FAST16_MIN __MLIBC_INT64_MIN
+
+typedef __mlibc_int64 __mlibc_int_fast32;
+#define __MLIBC_INT_FAST32_C(x) __MLIBC_INT64_C(x)
+#define __MLIBC_INT_FAST32_MAX __MLIBC_INT64_MAX
+#define __MLIBC_INT_FAST32_MIN __MLIBC_INT64_MIN
+
+typedef __mlibc_int64 __mlibc_int_fast64;
+#define __MLIBC_INT_FAST64_C(x) __MLIBC_INT64_C(x)
+#define __MLIBC_INT_FAST64_MAX __MLIBC_INT64_MAX
+#define __MLIBC_INT_FAST64_MIN __MLIBC_INT64_MIN
+
+#else
+# error "Missing architecture specific code"
+#endif
+
+// Fast types (unsigned).
+
+#if defined (__i386__)
+
+typedef __mlibc_uint8 __mlibc_uint_fast8;
+#define __MLIBC_UINT_FAST8_C(x) __MLIBC_UINT8_C(x)
+#define __MLIBC_UINT_FAST8_MAX __MLIBC_UINT8_MAX
+#define __MLIBC_UINT_FAST8_MIN __MLIBC_UINT8_MIN
+
+typedef __mlibc_uint32 __mlibc_uint_fast16;
+#define __MLIBC_UINT_FAST16_C(x) __MLIBC_UINT32_C(x)
+#define __MLIBC_UINT_FAST16_MAX __MLIBC_UINT32_MAX
+#define __MLIBC_UINT_FAST16_MIN __MLIBC_UINT32_MIN
+
+typedef __mlibc_uint32 __mlibc_uint_fast32;
+#define __MLIBC_UINT_FAST32_C(x) __MLIBC_UINT32_C(x)
+#define __MLIBC_UINT_FAST32_MAX __MLIBC_UINT32_MAX
+#define __MLIBC_UINT_FAST32_MIN __MLIBC_UINT32_MIN
+
+typedef __mlibc_uint64 __mlibc_uint_fast64;
+#define __MLIBC_UINT_FAST64_C(x) __MLIBC_UINT64_C(x)
+#define __MLIBC_UINT_FAST64_MAX __MLIBC_UINT64_MAX
+#define __MLIBC_UINT_FAST64_MIN __MLIBC_UINT64_MIN
+
+#elif defined (__x86_64__)
+
+typedef __mlibc_uint8 __mlibc_uint_fast8;
+#define __MLIBC_UINT_FAST8_C(x) __MLIBC_UINT8_C(x)
+#define __MLIBC_UINT_FAST8_MAX __MLIBC_UINT8_MAX
+#define __MLIBC_UINT_FAST8_MIN __MLIBC_UINT8_MIN
+
+typedef __mlibc_uint64 __mlibc_uint_fast16;
+#define __MLIBC_UINT_FAST16_C(x) __MLIBC_UINT64_C(x)
+#define __MLIBC_UINT_FAST16_MAX __MLIBC_UINT64_MAX
+#define __MLIBC_UINT_FAST16_MIN __MLIBC_UINT64_MIN
+
+typedef __mlibc_uint64 __mlibc_uint_fast32;
+#define __MLIBC_UINT_FAST32_C(x) __MLIBC_UINT64_C(x)
+#define __MLIBC_UINT_FAST32_MAX __MLIBC_UINT64_MAX
+#define __MLIBC_UINT_FAST32_MIN __MLIBC_UINT64_MIN
+
+typedef __mlibc_uint64 __mlibc_uint_fast64;
+#define __MLIBC_UINT_FAST64_C(x) __MLIBC_UINT64_C(x)
+#define __MLIBC_UINT_FAST64_MAX __MLIBC_UINT64_MAX
+#define __MLIBC_UINT_FAST64_MIN __MLIBC_UINT64_MIN
+
+#elif defined (__aarch64__)
+
+typedef __mlibc_uint8 __mlibc_uint_fast8;
+#define __MLIBC_UINT_FAST8_C(x) __MLIBC_UINT8_C(x)
+#define __MLIBC_UINT_FAST8_MAX __MLIBC_UINT8_MAX
+#define __MLIBC_UINT_FAST8_MIN __MLIBC_UINT8_MIN
+
+typedef __mlibc_uint64 __mlibc_uint_fast16;
+#define __MLIBC_UINT_FAST16_C(x) __MLIBC_UINT64_C(x)
+#define __MLIBC_UINT_FAST16_MAX __MLIBC_UINT64_MAX
+#define __MLIBC_UINT_FAST16_MIN __MLIBC_UINT64_MIN
+
+typedef __mlibc_uint64 __mlibc_uint_fast32;
+#define __MLIBC_UINT_FAST32_C(x) __MLIBC_UINT64_C(x)
+#define __MLIBC_UINT_FAST32_MAX __MLIBC_UINT64_MAX
+#define __MLIBC_UINT_FAST32_MIN __MLIBC_UINT64_MIN
+
+typedef __mlibc_uint64 __mlibc_uint_fast64;
+#define __MLIBC_UINT_FAST64_C(x) __MLIBC_UINT64_C(x)
+#define __MLIBC_UINT_FAST64_MAX __MLIBC_UINT64_MAX
+#define __MLIBC_UINT_FAST64_MIN __MLIBC_UINT64_MIN
+
+#elif defined (__riscv) && __riscv_xlen == 64
+
+typedef __mlibc_uint8 __mlibc_uint_fast8;
+#define __MLIBC_UINT_FAST8_C(x) __MLIBC_UINT8_C(x)
+#define __MLIBC_UINT_FAST8_MAX __MLIBC_UINT8_MAX
+#define __MLIBC_UINT_FAST8_MIN __MLIBC_UINT8_MIN
+
+typedef __mlibc_uint64 __mlibc_uint_fast16;
+#define __MLIBC_UINT_FAST16_C(x) __MLIBC_UINT64_C(x)
+#define __MLIBC_UINT_FAST16_MAX __MLIBC_UINT64_MAX
+#define __MLIBC_UINT_FAST16_MIN __MLIBC_UINT64_MIN
+
+typedef __mlibc_uint64 __mlibc_uint_fast32;
+#define __MLIBC_UINT_FAST32_C(x) __MLIBC_UINT64_C(x)
+#define __MLIBC_UINT_FAST32_MAX __MLIBC_UINT64_MAX
+#define __MLIBC_UINT_FAST32_MIN __MLIBC_UINT64_MIN
+
+typedef __mlibc_uint64 __mlibc_uint_fast64;
+#define __MLIBC_UINT_FAST64_C(x) __MLIBC_UINT64_C(x)
+#define __MLIBC_UINT_FAST64_MAX __MLIBC_UINT64_MAX
+#define __MLIBC_UINT_FAST64_MIN __MLIBC_UINT64_MIN
+
+#else
+# error "Missing architecture specific code"
+#endif
+
+// Special types.
+
+typedef __INTMAX_TYPE__ __mlibc_intmax;
+typedef __INTPTR_TYPE__ __mlibc_intptr;
+typedef __PTRDIFF_TYPE__ __mlibc_ptrdiff;
+#define __MLIBC_INTMAX_MAX __INTMAX_MAX__
+#define __MLIBC_INTMAX_MIN (-__INTMAX_MAX__ - 1)
+#define __MLIBC_INTPTR_MAX __INTPTR_MAX__
+#define __MLIBC_INTPTR_MIN (-__INTPTR_MAX__ - 1)
+#define __MLIBC_PTRDIFF_MAX __PTRDIFF_MAX__
+#define __MLIBC_PTRDIFF_MIN (-__PTRDIFF_MAX__ - 1)
+
+typedef __UINTMAX_TYPE__ __mlibc_uintmax;
+typedef __UINTPTR_TYPE__ __mlibc_uintptr;
+typedef __SIZE_TYPE__ __mlibc_size;
+#define __MLIBC_UINTMAX_MAX __UINTMAX_MAX__
+#define __MLIBC_UINTPTR_MAX __UINTPTR_MAX__
+#define __MLIBC_SIZE_MAX __SIZE_MAX__
+
+// Other limits.
+
+#define __MLIBC_WCHAR_MAX __WCHAR_MAX__
+#define __MLIBC_WCHAR_MIN __WCHAR_MIN__
+
+#define __MLIBC_WINT_MAX __WINT_MAX__
+#define __MLIBC_WINT_MIN __WINT_MIN__
+
+#define __MLIBC_SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__
+#define __MLIBC_SIG_ATOMIC_MIN __SIG_ATOMIC_MIN__
+
+// ----------------------------------------------------------------------------
+// Sanity checking. Make sure that we agree with the compiler's ABI.
+// ----------------------------------------------------------------------------
+
+#if defined(__cpp_static_assert)
+# define __MLIBC_STATIC_ASSERT(c, text) static_assert(c, text)
+#elif !defined(__cplusplus)
+# define __MLIBC_STATIC_ASSERT(c, text) _Static_assert(c, text)
+#else
+# define __MLIBC_STATIC_ASSERT(c, text)
+#endif
+
+#define __MLIBC_CHECK_TYPE(T1, T2) __MLIBC_STATIC_ASSERT(sizeof(T1) == sizeof(T2),\
+ #T1 " != " #T2);
+
+// Least-width.
+__MLIBC_CHECK_TYPE(__mlibc_int8, __INT_LEAST8_TYPE__);
+__MLIBC_CHECK_TYPE(__mlibc_int16, __INT_LEAST16_TYPE__);
+__MLIBC_CHECK_TYPE(__mlibc_int32, __INT_LEAST32_TYPE__);
+__MLIBC_CHECK_TYPE(__mlibc_int64, __INT_LEAST64_TYPE__);
+
+__MLIBC_CHECK_TYPE(__mlibc_uint8, __UINT_LEAST8_TYPE__);
+__MLIBC_CHECK_TYPE(__mlibc_uint16, __UINT_LEAST16_TYPE__);
+__MLIBC_CHECK_TYPE(__mlibc_uint32, __UINT_LEAST32_TYPE__);
+__MLIBC_CHECK_TYPE(__mlibc_uint64, __UINT_LEAST64_TYPE__);
+
+// Fast-width.
+// Unfortunately, GCC and Clang disagree about fast types.
+#ifndef __clang__
+ __MLIBC_CHECK_TYPE(__mlibc_int_fast8, __INT_FAST8_TYPE__);
+ __MLIBC_CHECK_TYPE(__mlibc_int_fast16, __INT_FAST16_TYPE__);
+ __MLIBC_CHECK_TYPE(__mlibc_int_fast32, __INT_FAST32_TYPE__);
+ __MLIBC_CHECK_TYPE(__mlibc_int_fast64, __INT_FAST64_TYPE__);
+
+ __MLIBC_CHECK_TYPE(__mlibc_uint_fast8, __UINT_FAST8_TYPE__);
+ __MLIBC_CHECK_TYPE(__mlibc_uint_fast16, __UINT_FAST16_TYPE__);
+ __MLIBC_CHECK_TYPE(__mlibc_uint_fast32, __UINT_FAST32_TYPE__);
+ __MLIBC_CHECK_TYPE(__mlibc_uint_fast64, __UINT_FAST64_TYPE__);
+#endif
+
+#endif // _MLIBC_INTERNAL_TYPES_H
diff --git a/lib/mlibc/options/internal/include/bits/wchar.h b/lib/mlibc/options/internal/include/bits/wchar.h
new file mode 100644
index 0000000..a422ef7
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/wchar.h
@@ -0,0 +1,9 @@
+#ifndef MLIBC_WCHAR_H
+#define MLIBC_WCHAR_H
+
+#include <bits/types.h>
+
+#define WCHAR_MAX __MLIBC_WCHAR_MAX
+#define WCHAR_MIN __MLIBC_WCHAR_MIN
+
+#endif // MLIBC_WCHAR_H
diff --git a/lib/mlibc/options/internal/include/bits/wchar_t.h b/lib/mlibc/options/internal/include/bits/wchar_t.h
new file mode 100644
index 0000000..4eb4e9c
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/wchar_t.h
@@ -0,0 +1,12 @@
+
+#ifndef MLIBC_WCHAR_T_H
+#define MLIBC_WCHAR_T_H
+
+#ifndef __cplusplus
+
+typedef __WCHAR_TYPE__ wchar_t;
+
+#endif
+
+#endif // MLIBC_WCHAR_T_H
+
diff --git a/lib/mlibc/options/internal/include/bits/winsize.h b/lib/mlibc/options/internal/include/bits/winsize.h
new file mode 100644
index 0000000..7b3006a
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/winsize.h
@@ -0,0 +1,13 @@
+
+#ifndef MLIBC_WINSIZE_H
+#define MLIBC_WINSIZE_H
+
+struct winsize {
+ unsigned short ws_row;
+ unsigned short ws_col;
+ unsigned short ws_xpixel;
+ unsigned short ws_ypixel;
+};
+
+#endif // MLIBC_WINSIZE_H
+
diff --git a/lib/mlibc/options/internal/include/bits/wint_t.h b/lib/mlibc/options/internal/include/bits/wint_t.h
new file mode 100644
index 0000000..b4f57bf
--- /dev/null
+++ b/lib/mlibc/options/internal/include/bits/wint_t.h
@@ -0,0 +1,6 @@
+#ifndef MLIBC_WINT_T_H
+#define MLIBC_WINT_T_H
+
+typedef __WINT_TYPE__ wint_t;
+
+#endif // MLIBC_WINT_T_H
diff --git a/lib/mlibc/options/internal/include/mlibc/all-sysdeps.hpp b/lib/mlibc/options/internal/include/mlibc/all-sysdeps.hpp
new file mode 100644
index 0000000..7189286
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/all-sysdeps.hpp
@@ -0,0 +1,33 @@
+#ifndef MLIBC_ALL_SYSDEPS
+#define MLIBC_ALL_SYSDEPS
+
+#include <mlibc-config.h>
+#include <internal-config.h>
+
+#if __MLIBC_ANSI_OPTION
+# include <mlibc/ansi-sysdeps.hpp>
+#endif /* __MLIBC_ANSI_OPTION */
+
+#if __MLIBC_POSIX_OPTION
+# include <mlibc/posix-sysdeps.hpp>
+#endif /* __MLIBC_POSIX_OPTION */
+
+#if __MLIBC_LINUX_OPTION
+# include <mlibc/linux-sysdeps.hpp>
+#endif /* __MLIBC_LINUX_OPTION */
+
+#if __MLIBC_GLIBC_OPTION
+# include <mlibc/glibc-sysdeps.hpp>
+#endif /* __MLIBC_GLIBC_OPTION */
+
+#if __MLIBC_BSD_OPTION
+# include <mlibc/bsd-sysdeps.hpp>
+#endif /* __MLIBC_BSD_OPTION */
+
+#if MLIBC_BUILDING_RTDL
+# include <mlibc/rtdl-sysdeps.hpp>
+#endif /* MLIBC_BUILDING_RTDL */
+
+#include <mlibc/internal-sysdeps.hpp>
+
+#endif /* MLIBC_ALL_SYSDEPS */
diff --git a/lib/mlibc/options/internal/include/mlibc/allocator.hpp b/lib/mlibc/options/internal/include/mlibc/allocator.hpp
new file mode 100644
index 0000000..5f9617e
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/allocator.hpp
@@ -0,0 +1,37 @@
+#ifndef MLIBC_FRIGG_ALLOC
+#define MLIBC_FRIGG_ALLOC
+
+#include <mlibc/lock.hpp>
+#include <bits/ensure.h>
+#include <frg/slab.hpp>
+#include <internal-config.h>
+
+#if !MLIBC_DEBUG_ALLOCATOR
+
+struct VirtualAllocator {
+public:
+ uintptr_t map(size_t length);
+
+ void unmap(uintptr_t address, size_t length);
+};
+
+typedef frg::slab_pool<VirtualAllocator, FutexLock> MemoryPool;
+
+typedef frg::slab_allocator<VirtualAllocator, FutexLock> MemoryAllocator;
+
+MemoryAllocator &getAllocator();
+
+#else
+
+struct MemoryAllocator {
+ void *allocate(size_t size);
+ void free(void *ptr);
+ void deallocate(void *ptr, size_t size);
+ void *reallocate(void *ptr, size_t size);
+};
+
+MemoryAllocator &getAllocator();
+
+#endif // !MLIBC_DEBUG_ALLOCATOR
+
+#endif // MLIBC_FRIGG_ALLOC
diff --git a/lib/mlibc/options/internal/include/mlibc/bitutil.hpp b/lib/mlibc/options/internal/include/mlibc/bitutil.hpp
new file mode 100644
index 0000000..6d2b25e
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/bitutil.hpp
@@ -0,0 +1,34 @@
+#ifndef MLIBC_BITUTIL
+#define MLIBC_BITUTIL
+
+#include <stdint.h>
+
+namespace mlibc {
+
+template<typename T>
+struct bit_util;
+
+template<>
+struct bit_util<uint64_t> {
+ static uint64_t byteswap(uint64_t x) {
+ return __builtin_bswap64(x);
+ }
+};
+
+template<>
+struct bit_util<uint32_t> {
+ static uint32_t byteswap(uint32_t x) {
+ return __builtin_bswap32(x);
+ }
+};
+
+template<>
+struct bit_util<uint16_t> {
+ static uint16_t byteswap(uint16_t x) {
+ return __builtin_bswap16(x);
+ }
+};
+
+} // namespace mlibc
+
+#endif // MLIBC_BITUTIL
diff --git a/lib/mlibc/options/internal/include/mlibc/charcode.hpp b/lib/mlibc/options/internal/include/mlibc/charcode.hpp
new file mode 100644
index 0000000..67bd03d
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/charcode.hpp
@@ -0,0 +1,124 @@
+#ifndef MLIBC_CHARCODE_HPP
+#define MLIBC_CHARCODE_HPP
+
+#include <stddef.h>
+#include <stdint.h>
+#include <bits/ensure.h>
+#include <bits/mbstate.h>
+#include <mlibc/debug.hpp>
+
+namespace mlibc {
+
+enum class charcode_error {
+ null,
+ dirty,
+ illegal_input,
+ input_underflow,
+ output_overflow
+};
+
+template<typename C>
+struct code_seq {
+ C *it;
+ const C *end;
+
+ explicit operator bool () {
+ return it != end;
+ }
+};
+
+// Some encodings (e.g. the one defined in RFC 1843) have "shift states",
+// i.e. escape sequences that switch between different encodings (e.g. between single-byte ASCII
+// and 2-byte encoding of Chinese characters).
+// TODO: Implement that using the __shift member of __mlibc_mbstate.
+
+typedef uint32_t codepoint;
+
+// The following class deals with decoding/encoding "code units" (of type char)
+// to "code points" that are defined by unicode (of type codepoint).
+// It also offers convenience functions to transcode to wchar_t, char16_t and char32_t.
+// We assume that the encoding of wchar_t (and char16_t, char32_t) is fixed.
+// char is allowed to have an arbitrary encoding.
+// TODO: char16_t and char32_t variants are missing.
+// TODO: For iconv(), first decode and then encode to the destination encoding.
+struct polymorphic_charcode {
+ virtual ~polymorphic_charcode();
+
+ // Helper function to decode a single char.
+ charcode_error promote(char nc, codepoint &wc) {
+ auto uc = static_cast<unsigned char>(nc);
+ if(uc <= 0x7F && preserves_7bit_units) {
+ wc = uc;
+ return charcode_error::null;
+ }
+
+ code_seq<const char> nseq{&nc, &nc + 1};
+ code_seq<codepoint> wseq{&wc, &wc + 1};
+ __mlibc_mbstate st = __MLIBC_MBSTATE_INITIALIZER;
+
+ if(auto e = decode(nseq, wseq, st); e != charcode_error::null)
+ return e;
+ // This should have read/written exactly one code unit/code point.
+ __ensure(nseq.it == nseq.end);
+ __ensure(wseq.it == wseq.end);
+ return charcode_error::null;
+ }
+
+ // Helper function to decode a single char.
+ charcode_error promote_wtranscode(char nc, wchar_t &wc) {
+ auto uc = static_cast<unsigned char>(nc);
+ if(uc <= 0x7F && preserves_7bit_units) { // TODO: Use "wtranscode_preserves_7bit_units".
+ wc = uc;
+ return charcode_error::null;
+ }
+
+ code_seq<const char> nseq{&nc, &nc + 1};
+ code_seq<wchar_t> wseq{&wc, &wc + 1};
+ __mlibc_mbstate st = __MLIBC_MBSTATE_INITIALIZER;
+
+ if(auto e = decode_wtranscode(nseq, wseq, st); e != charcode_error::null)
+ return e;
+ // This should have read/written exactly one code unit/code point.
+ __ensure(nseq.it == nseq.end);
+ __ensure(wseq.it == wseq.end);
+ return charcode_error::null;
+ }
+
+ polymorphic_charcode(bool preserves_7bit_units_, bool has_shift_states_)
+ : preserves_7bit_units{preserves_7bit_units_}, has_shift_states{has_shift_states_} { }
+
+ virtual charcode_error decode(code_seq<const char> &nseq, code_seq<codepoint> &wseq,
+ __mlibc_mbstate &st) = 0;
+
+ virtual charcode_error decode_wtranscode(code_seq<const char> &nseq, code_seq<wchar_t> &wseq,
+ __mlibc_mbstate &st) = 0;
+
+ virtual charcode_error decode_wtranscode_length(code_seq<const char> &nseq, size_t *n,
+ __mlibc_mbstate &st) = 0;
+
+ virtual charcode_error encode_wtranscode(code_seq<char> &nseq, code_seq<const wchar_t> &wseq,
+ __mlibc_mbstate &st) = 0;
+
+ virtual charcode_error encode_wtranscode_length(code_seq<const wchar_t> &wseq, size_t *n,
+ __mlibc_mbstate &st) = 0;
+
+ // True if promotion only zero-extends units below 0x7F.
+ const bool preserves_7bit_units;
+
+ // Whether the encoding has shift states.
+ const bool has_shift_states;
+};
+
+polymorphic_charcode *current_charcode();
+
+// Similar to polymorphic_charcode but for wchar_t. Note that this encoding is fixed per-platform;
+// thus, it does not need to be polymorphic.
+struct wide_charcode {
+ charcode_error promote(wchar_t nc, codepoint &wc);
+};
+
+wide_charcode *platform_wide_charcode();
+
+} // namespace mlibc
+
+#endif // MLIBC_CHARCODE_HPP
diff --git a/lib/mlibc/options/internal/include/mlibc/charset.hpp b/lib/mlibc/options/internal/include/mlibc/charset.hpp
new file mode 100644
index 0000000..a068f05
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/charset.hpp
@@ -0,0 +1,40 @@
+#ifndef MLIBC_CHARSET_HPP
+#define MLIBC_CHARSET_HPP
+
+#include <mlibc/charcode.hpp>
+
+namespace mlibc {
+
+// Represents the charset of a certain locale. We define the charset as
+// a set of characters, together with their properties and conversion rules
+// *but not* their encoding (e.g. to UTF-8 or UTF-16).
+struct charset {
+ // Returns true iif the meaning of the first 0x7F characters matches ASCII.
+ bool is_ascii_superset();
+
+ bool is_alpha(codepoint c);
+ bool is_digit(codepoint c);
+ bool is_xdigit(codepoint c);
+ bool is_alnum(codepoint c);
+ bool is_punct(codepoint c);
+ bool is_graph(codepoint c);
+ bool is_blank(codepoint c);
+ bool is_space(codepoint c);
+ bool is_print(codepoint c);
+
+ bool is_lower(codepoint c);
+ bool is_upper(codepoint c);
+ codepoint to_lower(codepoint c);
+ codepoint to_upper(codepoint c);
+};
+
+charset *current_charset();
+
+// The property if a character is a control character is locale-independent.
+inline bool generic_is_control(codepoint c) {
+ return (c <= 0x1F) || (c == 0x7F) || (c >= 0x80 && c <= 0x9F);
+}
+
+} // namespace mlibc
+
+#endif // MLIBC_CHARSET_HPP
diff --git a/lib/mlibc/options/internal/include/mlibc/debug.hpp b/lib/mlibc/options/internal/include/mlibc/debug.hpp
new file mode 100644
index 0000000..7067039
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/debug.hpp
@@ -0,0 +1,27 @@
+#ifndef MLIBC_DEBUG_HPP
+#define MLIBC_DEBUG_HPP
+
+#include <frg/logging.hpp>
+
+namespace mlibc {
+
+struct InfoSink {
+ // constexpr so that this can be initialized statically.
+ constexpr InfoSink() = default;
+
+ void operator() (const char *message);
+};
+
+struct PanicSink {
+ // constexpr so that this can be initialized statically.
+ constexpr PanicSink() = default;
+
+ void operator() (const char *message);
+};
+
+extern frg::stack_buffer_logger<InfoSink, 512> infoLogger;
+extern frg::stack_buffer_logger<PanicSink, 512> panicLogger;
+
+} // namespace mlibc
+
+#endif // MLIBC_DEBUG_HPP
diff --git a/lib/mlibc/options/internal/include/mlibc/file-window.hpp b/lib/mlibc/options/internal/include/mlibc/file-window.hpp
new file mode 100644
index 0000000..68c3ebf
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/file-window.hpp
@@ -0,0 +1,64 @@
+#ifndef MLIBC_FILE_WINDOW
+#define MLIBC_FILE_WINDOW
+
+#include <abi-bits/fcntl.h>
+#include <mlibc/allocator.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/internal-sysdeps.hpp>
+#include <internal-config.h>
+
+struct file_window {
+ file_window(const char *path) {
+ int fd;
+ if(mlibc::sys_open("/etc/localtime", O_RDONLY, 0, &fd))
+ mlibc::panicLogger() << "mlibc: Error opening file_window to "
+ << path << frg::endlog;
+
+ if(!mlibc::sys_stat) {
+ MLIBC_MISSING_SYSDEP();
+ __ensure(!"cannot proceed without sys_stat");
+ }
+ struct stat info;
+ if(mlibc::sys_stat(mlibc::fsfd_target::fd, fd, "", 0, &info))
+ mlibc::panicLogger() << "mlibc: Error getting TZinfo stats" << frg::endlog;
+
+#if MLIBC_MAP_FILE_WINDOWS
+ if(mlibc::sys_vm_map(nullptr, (size_t)info.st_size, PROT_READ, MAP_PRIVATE,
+ fd, 0, &_ptr))
+ mlibc::panicLogger() << "mlibc: Error mapping TZinfo" << frg::endlog;
+#else
+ _ptr = getAllocator().allocate(info.st_size);
+ __ensure(_ptr);
+
+ size_t progress = 0;
+ size_t st_size = static_cast<size_t>(info.st_size);
+ while(progress < st_size) {
+ ssize_t chunk;
+ if(int e = mlibc::sys_read(fd, reinterpret_cast<char *>(_ptr) + progress,
+ st_size - progress, &chunk); e)
+ mlibc::panicLogger() << "mlibc: Read from file_window failed" << frg::endlog;
+ if(!chunk)
+ break;
+ progress += chunk;
+ }
+ if(progress != st_size)
+ mlibc::panicLogger() << "stat reports " << info.st_size << " but we only read "
+ << progress << " bytes" << frg::endlog;
+#endif
+
+ if(mlibc::sys_close(fd))
+ mlibc::panicLogger() << "mlibc: Error closing TZinfo" << frg::endlog;
+ }
+
+ // TODO: Write destructor to deallocate/unmap memory.
+
+ void *get() {
+ return _ptr;
+ }
+
+private:
+ void *_ptr;
+};
+
+#endif // MLIBC_FILE_WINDOW
+
diff --git a/lib/mlibc/options/internal/include/mlibc/fsfd_target.hpp b/lib/mlibc/options/internal/include/mlibc/fsfd_target.hpp
new file mode 100644
index 0000000..b577325
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/fsfd_target.hpp
@@ -0,0 +1,15 @@
+#ifndef MLIBC_FSFD_TARGET
+#define MLIBC_FSFD_TARGET
+
+namespace mlibc {
+
+enum class fsfd_target {
+ none,
+ path,
+ fd,
+ fd_path
+};
+
+} // namespace mlibc
+
+#endif // MLIBC_FSFD_TARGET
diff --git a/lib/mlibc/options/internal/include/mlibc/global-config.hpp b/lib/mlibc/options/internal/include/mlibc/global-config.hpp
new file mode 100644
index 0000000..7eaed3c
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/global-config.hpp
@@ -0,0 +1,19 @@
+#ifndef MLIBC_GLOBAL_CONFIG
+#define MLIBC_GLOBAL_CONFIG
+
+namespace mlibc {
+
+struct GlobalConfig {
+ GlobalConfig();
+
+ bool debugMalloc;
+};
+
+inline const GlobalConfig &globalConfig() {
+ static GlobalConfig cached;
+ return cached;
+}
+
+}
+
+#endif // MLIBC_GLOBAL_CONFIG
diff --git a/lib/mlibc/options/internal/include/mlibc/internal-sysdeps.hpp b/lib/mlibc/options/internal/include/mlibc/internal-sysdeps.hpp
new file mode 100644
index 0000000..02df713
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/internal-sysdeps.hpp
@@ -0,0 +1,41 @@
+#ifndef MLIBC_INTERNAL_SYSDEPS
+#define MLIBC_INTERNAL_SYSDEPS
+
+#include <stddef.h>
+
+#include <abi-bits/seek-whence.h>
+#include <abi-bits/vm-flags.h>
+#include <bits/off_t.h>
+#include <bits/ssize_t.h>
+#include <abi-bits/stat.h>
+#include <mlibc/fsfd_target.hpp>
+
+namespace [[gnu::visibility("hidden")]] mlibc {
+
+void sys_libc_log(const char *message);
+[[noreturn]] void sys_libc_panic();
+
+int sys_tcb_set(void *pointer);
+
+[[gnu::weak]] int sys_futex_tid();
+int sys_futex_wait(int *pointer, int expected, const struct timespec *time);
+int sys_futex_wake(int *pointer);
+
+int sys_anon_allocate(size_t size, void **pointer);
+int sys_anon_free(void *pointer, size_t size);
+
+int sys_open(const char *pathname, int flags, mode_t mode, int *fd);
+int sys_read(int fd, void *buf, size_t count, ssize_t *bytes_read);
+int sys_seek(int fd, off_t offset, int whence, off_t *new_offset);
+int sys_close(int fd);
+
+[[gnu::weak]] int sys_stat(fsfd_target fsfdt, int fd, const char *path, int flags,
+ struct stat *statbuf);
+// mlibc assumes that anonymous memory returned by sys_vm_map() is zeroed by the kernel / whatever is behind the sysdeps
+int sys_vm_map(void *hint, size_t size, int prot, int flags, int fd, off_t offset, void **window);
+int sys_vm_unmap(void *pointer, size_t size);
+[[gnu::weak]] int sys_vm_protect(void *pointer, size_t size, int prot);
+
+} //namespace mlibc
+
+#endif // MLIBC_INTERNAL_SYSDEPS
diff --git a/lib/mlibc/options/internal/include/mlibc/locale.hpp b/lib/mlibc/options/internal/include/mlibc/locale.hpp
new file mode 100644
index 0000000..a46a2c3
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/locale.hpp
@@ -0,0 +1,12 @@
+#ifndef MLIBC_LOCALE
+#define MLIBC_LOCALE
+
+#include <bits/nl_item.h>
+
+namespace mlibc {
+
+char *nl_langinfo(nl_item item);
+
+} // namespace mlibc
+
+#endif // MLIBC_LOCALE
diff --git a/lib/mlibc/options/internal/include/mlibc/lock.hpp b/lib/mlibc/options/internal/include/mlibc/lock.hpp
new file mode 100644
index 0000000..aa05079
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/lock.hpp
@@ -0,0 +1,124 @@
+#ifndef MLIBC_LOCK_HPP
+#define MLIBC_LOCK_HPP
+
+#include <errno.h>
+#include <stdint.h>
+#include <mlibc/internal-sysdeps.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/tid.hpp>
+#include <bits/ensure.h>
+
+template<bool Recursive>
+struct FutexLockImpl {
+ FutexLockImpl() : _state{0}, _recursion{0} { }
+
+ FutexLockImpl(const FutexLockImpl &) = delete;
+
+ FutexLockImpl &operator= (const FutexLockImpl &) = delete;
+
+ static constexpr uint32_t waitersBit = (1 << 31);
+ static constexpr uint32_t ownerMask = (static_cast<uint32_t>(1) << 30) - 1;
+
+ void lock() {
+ unsigned int this_tid = mlibc::this_tid();
+ unsigned int expected = 0;
+
+ while(true) {
+ if(!expected) {
+ // Try to take the mutex here.
+ if(__atomic_compare_exchange_n(&_state,
+ &expected, this_tid, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) {
+ if constexpr (Recursive) {
+ __ensure(!_recursion);
+ _recursion = 1;
+ }
+ return;
+ }
+ }else{
+ // If this (recursive) mutex is already owned by us, increment the recursion level.
+ if((expected & ownerMask) == this_tid) {
+ if constexpr (Recursive)
+ ++_recursion;
+ else
+ mlibc::panicLogger() << "mlibc: FutexLock deadlock detected!" << frg::endlog;
+ return;
+ }
+
+ // Wait on the futex if the waiters flag is set.
+ if(expected & waitersBit) {
+ int e = mlibc::sys_futex_wait((int *)&_state, expected, nullptr);
+
+ // If the wait returns EAGAIN, that means that the waitersBit was just unset by
+ // some other thread. In this case, we should loop back around.
+ if (e && e != EAGAIN)
+ mlibc::panicLogger() << "sys_futex_wait() failed with error code " << e << frg::endlog;
+
+ // Opportunistically try to take the lock after we wake up.
+ expected = 0;
+ }else{
+ // Otherwise we have to set the waiters flag first.
+ unsigned int desired = expected | waitersBit;
+ if(__atomic_compare_exchange_n((int *)&_state,
+ reinterpret_cast<int*>(&expected), desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED))
+ expected = desired;
+ }
+ }
+ }
+ }
+
+ bool try_lock() {
+ unsigned int this_tid = mlibc::this_tid();
+ unsigned int expected = __atomic_load_n(&_state, __ATOMIC_RELAXED);
+
+ if(!expected) {
+ // Try to take the mutex here.
+ if(__atomic_compare_exchange_n(&_state,
+ &expected, this_tid, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) {
+ if constexpr (Recursive)
+ _recursion = 1;
+ return true;
+ }
+ } else {
+ // If this (recursive) mutex is already owned by us, increment the recursion level.
+ if((expected & ownerMask) == this_tid) {
+ if constexpr (Recursive) {
+ __ensure(!_recursion);
+ ++_recursion;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ void unlock() {
+ // Decrement the recursion level and unlock if we hit zero.
+ if constexpr (Recursive) {
+ __ensure(_recursion);
+ if(--_recursion)
+ return;
+ }
+
+ // Reset the mutex to the unlocked state.
+ auto state = __atomic_exchange_n(&_state, 0, __ATOMIC_RELEASE);
+ __ensure((state & ownerMask) == mlibc::this_tid());
+
+ if(state & waitersBit) {
+ // Wake the futex if there were waiters. Since the mutex might not exist at this location
+ // anymore, we must conservatively ignore EACCES and EINVAL which may occur as a result.
+ int e = mlibc::sys_futex_wake((int *)&_state);
+ __ensure(e >= 0 || e == EACCES || e == EINVAL);
+ }
+ }
+private:
+ uint32_t _state;
+ uint32_t _recursion;
+};
+
+using FutexLock = FutexLockImpl<false>;
+using RecursiveFutexLock = FutexLockImpl<true>;
+
+#endif
diff --git a/lib/mlibc/options/internal/include/mlibc/stack_protector.hpp b/lib/mlibc/options/internal/include/mlibc/stack_protector.hpp
new file mode 100644
index 0000000..47290fc
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/stack_protector.hpp
@@ -0,0 +1,10 @@
+#ifndef MLIBC_STACK_PROTECTOR_HPP
+#define MLIBC_STACK_PROTECTOR_HPP
+
+namespace mlibc {
+
+void initStackGuard(void *);
+
+} // namespace mlibc
+
+#endif // MLIBC_STACK_PROTECTOR_HPP
diff --git a/lib/mlibc/options/internal/include/mlibc/strings.hpp b/lib/mlibc/options/internal/include/mlibc/strings.hpp
new file mode 100644
index 0000000..5a93c7c
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/strings.hpp
@@ -0,0 +1,12 @@
+#ifndef MLIBC_STRINGS
+#define MLIBC_STRINGS
+
+#include <bits/size_t.h>
+
+namespace mlibc {
+
+int strncasecmp(const char *a, const char *b, size_t size);
+
+} // namespace mlibc
+
+#endif // MLIBC_STRINGS
diff --git a/lib/mlibc/options/internal/include/mlibc/strtofp.hpp b/lib/mlibc/options/internal/include/mlibc/strtofp.hpp
new file mode 100644
index 0000000..f9c5e20
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/strtofp.hpp
@@ -0,0 +1,165 @@
+#ifndef MLIBC_STRTOFP_HPP
+#define MLIBC_STRTOFP_HPP
+
+#include <string.h>
+#include <bits/ensure.h>
+#include <type_traits>
+
+namespace mlibc {
+
+template<typename T>
+T strtofp(const char *str, char **endptr) {
+ if (strcmp(str, "INF") == 0 || strcmp(str, "inf") == 0) {
+ if (endptr)
+ *endptr = (char *)str + 3;
+ if constexpr (std::is_same_v<T, float>)
+ return __builtin_inff();
+ else if constexpr (std::is_same_v<T, double>)
+ return __builtin_inf();
+ else
+ return __builtin_infl();
+ } else if (strcmp(str, "INFINITY") == 0 || strcmp(str, "infinity") == 0) {
+ if (endptr)
+ *endptr = (char *)str + 8;
+ if constexpr (std::is_same_v<T, float>)
+ return __builtin_inff();
+ else if constexpr (std::is_same_v<T, double>)
+ return __builtin_inf();
+ else
+ return __builtin_infl();
+ } else if (strncmp(str, "NAN", 3) == 0 || strncmp(str, "nan", 3) == 0) {
+ if (endptr)
+ *endptr = (char *)str + 3;
+ if constexpr (std::is_same_v<T, float>)
+ return __builtin_nanf("");
+ else if constexpr (std::is_same_v<T, double>)
+ return __builtin_nan("");
+ else
+ return __builtin_nanl("");
+ }
+
+ bool negative = *str == '-';
+ if (*str == '+' || *str == '-')
+ str++;
+
+ bool hex = false;
+ if (*str == '0' && (*(str + 1) == 'x' || *(str + 1) == 'X')) {
+ str += 2;
+ hex = true;
+ }
+
+ T result = static_cast<T>(0);
+
+ const char *tmp = str;
+
+ if (!hex) {
+ while (true) {
+ if (!isdigit(*tmp))
+ break;
+ result *= static_cast<T>(10);
+ result += static_cast<T>(*tmp - '0');
+ tmp++;
+ }
+ } else {
+ while (true) {
+ if (!isxdigit(*tmp))
+ break;
+ result *= static_cast<T>(16);
+ result += static_cast<T>(*tmp <= '9' ? (*tmp - '0') : (tolower(*tmp) - 'a' + 10));
+ tmp++;
+ }
+ }
+
+ if (*tmp == '.') {
+ tmp++;
+
+ if (!hex) {
+ T d = static_cast<T>(10);
+
+ while (true) {
+ if (!isdigit(*tmp))
+ break;
+ result += static_cast<T>(*tmp - '0') / d;
+ d *= static_cast<T>(10);
+ tmp++;
+ }
+ } else {
+ T d = static_cast<T>(16);
+
+ while (true) {
+ if (!isxdigit(*tmp))
+ break;
+ result += static_cast<T>(*tmp <= '9' ? (*tmp - '0') : (tolower(*tmp) - 'a' + 10)) / d;
+ d *= static_cast<T>(16);
+ tmp++;
+ }
+ }
+ }
+
+ if (!hex) {
+ if (*tmp == 'e' || *tmp == 'E') {
+ tmp++;
+
+ bool exp_negative = *tmp == '-';
+ if (*tmp == '+' || *tmp == '-')
+ tmp++;
+
+ int exp = 0;
+ while (true) {
+ if (!isdigit(*tmp))
+ break;
+ exp *= 10;
+ exp += *tmp - '0';
+ tmp++;
+ }
+
+ if (!exp_negative) {
+ for (int i = 0; i < exp; ++i) {
+ result *= static_cast<T>(10);
+ }
+ } else {
+ for (int i = 0; i < exp; ++i) {
+ result /= static_cast<T>(10);
+ }
+ }
+ }
+ } else {
+ if (*tmp == 'p' || *tmp == 'P') {
+ tmp++;
+
+ bool exp_negative = *tmp == '-';
+ if (*tmp == '+' || *tmp == '-')
+ tmp++;
+
+ int exp = 0;
+ while (true) {
+ if (!isdigit(*tmp))
+ break;
+ exp *= 10;
+ exp += *tmp - '0';
+ tmp++;
+ }
+
+ if (!exp_negative) {
+ for (int i = 0; i < exp; ++i) {
+ result *= static_cast<T>(2);
+ }
+ } else {
+ for (int i = 0; i < exp; ++i) {
+ result /= static_cast<T>(2);
+ }
+ }
+ }
+ }
+
+ if (endptr)
+ *endptr = const_cast<char *>(tmp);
+ if (negative)
+ result = -result;
+
+ return result;
+}
+
+}
+
+#endif // MLIBC_STRTOFP_HPP
diff --git a/lib/mlibc/options/internal/include/mlibc/strtol.hpp b/lib/mlibc/options/internal/include/mlibc/strtol.hpp
new file mode 100644
index 0000000..3b8fca9
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/strtol.hpp
@@ -0,0 +1,159 @@
+#ifndef MLIBC_STRTOL_HPP
+#define MLIBC_STRTOL_HPP
+
+#include <type_traits>
+#include <ctype.h>
+#include <wctype.h>
+#include <limits.h>
+
+namespace mlibc {
+
+template<typename T> struct int_limits {};
+
+template<>
+struct int_limits<long> {
+ static long max() { return LONG_MAX; }
+ static long min() { return LONG_MIN; }
+};
+
+template<>
+struct int_limits<unsigned long> {
+ static unsigned long max() { return ULONG_MAX; }
+ static unsigned long min() { return 0; }
+};
+
+template<>
+struct int_limits<long long> {
+ static long long max() { return LLONG_MAX; }
+ static long long min() { return LLONG_MIN; }
+};
+
+template<>
+struct int_limits<unsigned long long> {
+ static unsigned long long max() { return ULLONG_MAX; }
+ static unsigned long long min() { return 0; }
+};
+
+template<typename T> struct char_detail {};
+
+template<>
+struct char_detail<char> {
+ static bool isSpace(char c) { return isspace(c); }
+ static bool isDigit(char c) { return isdigit(c); }
+ static bool isHexDigit(char c) { return isxdigit(c); }
+ static bool isLower(char c) { return islower(c); }
+ static bool isUpper(char c) { return isupper(c); }
+};
+
+template<>
+struct char_detail<wchar_t> {
+ static bool isSpace(wchar_t c) { return iswspace(c); }
+ static bool isDigit(wchar_t c) { return iswdigit(c); }
+ static bool isHexDigit(wchar_t c) { return iswxdigit(c); }
+ static bool isLower(wchar_t c) { return iswlower(c); }
+ static bool isUpper(wchar_t c) { return iswupper(c); }
+};
+
+template<typename Char> Char widen(char c) { return static_cast<Char>(c); }
+
+template<typename Return, typename Char>
+Return stringToInteger(const Char *__restrict nptr, Char **__restrict endptr, int baseInt) {
+ using UnsignedReturn = std::make_unsigned_t<Return>;
+
+ auto base = static_cast<Return>(baseInt);
+ auto s = nptr;
+
+ if (base < 0 || base == 1) {
+ if (endptr)
+ *endptr = const_cast<Char *>(nptr);
+ return 0;
+ }
+
+ while (char_detail<Char>::isSpace(*s))
+ s++;
+
+ bool negative = false;
+ if (*s == widen<Char>('-')) {
+ negative = true;
+ s++;
+ } else if (*s == widen<Char>('+')) {
+ s++;
+ }
+
+
+ bool hasOctalPrefix = s[0] == widen<Char>('0');
+ bool hasHexPrefix = hasOctalPrefix && (s[1] == widen<Char>('x') || s[1] == widen<Char>('X'));
+
+ // There's two tricky cases we need to keep in mind here:
+ // 1. We should interpret "0x5" as hex 5 rather than octal 0.
+ // 2. We should interpret "0x" as octal 0 (and set endptr correctly).
+ // To deal with 2, we check the charcacter following the hex prefix.
+ if ((base == 0 || base == 16) && hasHexPrefix && char_detail<Char>::isHexDigit(s[2])) {
+ s += 2;
+ base = 16;
+ } else if ((base == 0 || base == 8) && hasOctalPrefix) {
+ base = 8;
+ } else if (base == 0) {
+ base = 10;
+ }
+
+ // Compute the range of acceptable values.
+ UnsignedReturn cutoff, cutlim;
+ if (std::is_unsigned_v<Return>) {
+ cutoff = int_limits<Return>::max() / base;
+ cutlim = int_limits<Return>::max() % base;
+ } else {
+ Return co = negative ? int_limits<Return>::min() : int_limits<Return>::max();
+ cutlim = negative ? -(co % base) : co % base;
+ co /= negative ? -base : base;
+ cutoff = co;
+ }
+
+ UnsignedReturn totalValue = 0;
+ bool convertedAny = false;
+ bool outOfRange = false;
+ for (Char c = *s; c != widen<Char>('\0'); c = *++s) {
+ UnsignedReturn digitValue;
+ if (char_detail<Char>::isDigit(c))
+ digitValue = c - widen<Char>('0');
+ else if (char_detail<Char>::isUpper(c))
+ digitValue = c - widen<Char>('A') + 10;
+ else if (char_detail<Char>::isLower(c))
+ digitValue = c - widen<Char>('a') + 10;
+ else
+ break;
+
+ if (digitValue >= static_cast<UnsignedReturn>(base))
+ break;
+
+ if (outOfRange) {
+ // The value is already known to be out of range, but we need to keep
+ // consuming characters until we can't (to set endptr correctly).
+ } else if (totalValue > cutoff || (totalValue == cutoff && digitValue > cutlim)) {
+ // The value will be out of range if we accumulate digitValue.
+ outOfRange = true;
+ } else {
+ totalValue = (totalValue * base) + digitValue;
+ convertedAny = true;
+ }
+ }
+
+ if (endptr)
+ *endptr = const_cast<Char *>(convertedAny ? s : nptr);
+
+ if (outOfRange) {
+ errno = ERANGE;
+
+ if (std::is_unsigned_v<Return>) {
+ return int_limits<Return>::max();
+ } else {
+ return negative ? int_limits<Return>::min() : int_limits<Return>::max();
+ }
+ }
+
+ return negative ? -totalValue : totalValue;
+}
+
+}
+
+#endif // MLIBC_STRTOL_HPP
diff --git a/lib/mlibc/options/internal/include/mlibc/tcb.hpp b/lib/mlibc/options/internal/include/mlibc/tcb.hpp
new file mode 100644
index 0000000..92aad7a
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/tcb.hpp
@@ -0,0 +1,180 @@
+#pragma once
+
+#include <stdint.h>
+#include <limits.h>
+#include <bits/size_t.h>
+#include <frg/array.hpp>
+
+#include "elf.hpp"
+
+/*
+ * Explanation of cancellation bits:
+ *
+ * tcbCancelEnableBit and tcbCancelAsyncBit should be self-explanatory,
+ * they are set if cancellation is enabled, or asynchronous, respectively.
+ *
+ * tcbCancelTriggerBit is set whenever a cancellation is triggered, which is
+ * in pthread_cancel() or in the signal handler. This bit is used by
+ * pthread_testcancel() to check whether a cancellation has been requested,
+ * and also by cancellable syscalls.
+ *
+ * tcbCancelingBit is set when a cancellation is currently being handled. This
+ * is to avoid a situation in which a cancellation handler gets interrupted by
+ * a SIGCANCEL and a second cancellation handler gets executed on top of the
+ * previous one. Right now this cannot happen, since we stay in signal handler
+ * context when canceling/exiting. In the future this might be done outside
+ * of a signal handler, in which case we shouldn't restart the cancellation process.
+ *
+ * tcbExitingBit is set when the thread starts the exit procedure. Currently
+ * this is just an exit, but in the future this will be a stack unwinding
+ * procedure, which shouldn't be reentered. Not currently set anywhere,
+ * may be done so in the future.
+ *
+ * TODO(geert): update this comment when we do unwinding in the exit procedure.
+ */
+
+namespace {
+ // Set when the cancellation is enabled
+ constexpr unsigned int tcbCancelEnableBit = 1 << 0;
+ // 1 - cancellation is asynchronous, 0 - cancellation is deferred
+ constexpr unsigned int tcbCancelAsyncBit = 1 << 1;
+ // Set when the thread has been cancelled
+ constexpr unsigned int tcbCancelTriggerBit = 1 << 2;
+ // Set when the thread is in the process of being cancelled.
+ constexpr unsigned int tcbCancelingBit = 1 << 3;
+ // Set when the thread is exiting.
+ constexpr unsigned int tcbExitingBit = 1 << 4;
+}
+
+namespace mlibc {
+ // Returns true when bitmask indicates thread has been asynchronously
+ // cancelled.
+ static constexpr bool tcb_async_cancelled(int value) {
+ return (value & (tcbCancelEnableBit | tcbCancelAsyncBit
+ | tcbCancelTriggerBit)) == (tcbCancelEnableBit
+ | tcbCancelAsyncBit | tcbCancelTriggerBit);
+ }
+
+ // Returns true when bitmask indicates async cancellation is enabled.
+ static constexpr bool tcb_async_cancel(int value) {
+ return (value & (tcbCancelEnableBit | tcbCancelAsyncBit))
+ == (tcbCancelEnableBit | tcbCancelAsyncBit);
+ }
+
+ // Returns true when bitmask indicates cancellation is enabled.
+ static constexpr bool tcb_cancel_enabled(int value) {
+ return (value & tcbCancelEnableBit);
+ }
+
+ // Returns true when bitmask indicates threas has been cancelled.
+ static constexpr bool tcb_cancelled(int value) {
+ return (value & (tcbCancelEnableBit | tcbCancelTriggerBit))
+ == (tcbCancelEnableBit | tcbCancelTriggerBit);
+ }
+
+#if !MLIBC_STATIC_BUILD && !MLIBC_BUILDING_RTDL
+ // In non-static builds, libc.so always has a TCB available.
+ constexpr bool tcb_available_flag = true;
+#else
+ // Otherwise this will be set to true after RTDL has initialized the TCB.
+ extern bool tcb_available_flag;
+#endif
+}
+
+enum class TcbThreadReturnValue {
+ Pointer,
+ Integer,
+};
+
+struct Tcb {
+ Tcb *selfPointer;
+ size_t dtvSize;
+ void **dtvPointers;
+ int tid;
+ int didExit;
+#if defined(__x86_64__)
+ uint8_t padding[8];
+#endif
+ uintptr_t stackCanary;
+ int cancelBits;
+
+ union {
+ void *voidPtr;
+ int intVal;
+ } returnValue;
+ TcbThreadReturnValue returnValueType;
+
+ struct AtforkHandler {
+ void (*prepare)(void);
+ void (*parent)(void);
+ void (*child)(void);
+
+ AtforkHandler *next;
+ AtforkHandler *prev;
+ };
+
+ AtforkHandler *atforkBegin;
+ AtforkHandler *atforkEnd;
+
+ struct CleanupHandler {
+ void (*func)(void *);
+ void *arg;
+
+ CleanupHandler *next;
+ CleanupHandler *prev;
+ };
+
+ CleanupHandler *cleanupBegin;
+ CleanupHandler *cleanupEnd;
+ int isJoinable;
+
+ struct LocalKey {
+ void *value;
+ uint64_t generation;
+ };
+ frg::array<LocalKey, PTHREAD_KEYS_MAX> *localKeys;
+
+ size_t stackSize;
+ void *stackAddr;
+ size_t guardSize;
+
+ inline void invokeThreadFunc(void *entry, void *user_arg) {
+ if(returnValueType == TcbThreadReturnValue::Pointer) {
+ auto func = reinterpret_cast<void *(*)(void *)>(entry);
+ returnValue.voidPtr = func(user_arg);
+ } else {
+ auto func = reinterpret_cast<int (*)(void *)>(entry);
+ returnValue.intVal = func(user_arg);
+ }
+ }
+};
+
+// There are a few places where we assume the layout of the TCB:
+#if defined(__x86_64__)
+// GCC expects the stack canary to be at fs:0x28.
+static_assert(offsetof(Tcb, stackCanary) == 0x28);
+// sysdeps/linux/x86_64/cp_syscall.S uses the offset of cancelBits.
+static_assert(offsetof(Tcb, cancelBits) == 0x30);
+#elif defined(__i386__)
+// GCC expects the stack canary to be at gs:0x14.
+// The offset differs from x86_64 due to the change in the pointer size
+// and removed padding before the stack canary.
+static_assert(offsetof(Tcb, stackCanary) == 0x14);
+// sysdeps/linux/x86/cp_syscall.S uses the offset of cancelBits.
+// It differs from x86_64 for the same reasons as the stack canary.
+static_assert(offsetof(Tcb, cancelBits) == 0x18);
+#elif defined(__aarch64__)
+// The thread pointer on AArch64 points to 16 bytes before the end of the TCB.
+// options/linker/aarch64/runtime.S uses the offset of dtvPointers.
+static_assert(sizeof(Tcb) - offsetof(Tcb, dtvPointers) - TP_TCB_OFFSET == 104);
+// sysdeps/linux/aarch64/cp_syscall.S uses the offset of cancelBits.
+static_assert(sizeof(Tcb) - offsetof(Tcb, cancelBits) - TP_TCB_OFFSET == 80);
+#elif defined(__riscv) && __riscv_xlen == 64
+// The thread pointer on RISC-V points to *after* the TCB, and since
+// we need to access specific fields that means that the value in
+// sysdeps/linux/riscv64/cp_syscall.S needs to be updated whenever
+// the struct is expanded.
+static_assert(sizeof(Tcb) - offsetof(Tcb, cancelBits) == 96);
+#else
+#error "Missing architecture specific code."
+#endif
diff --git a/lib/mlibc/options/internal/include/mlibc/threads.hpp b/lib/mlibc/options/internal/include/mlibc/threads.hpp
new file mode 100644
index 0000000..989a8e5
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/threads.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <bits/ansi/timespec.h>
+#include <bits/threads.h>
+
+namespace mlibc {
+
+int thread_create(struct __mlibc_thread_data **__restrict thread, const struct __mlibc_threadattr *__restrict attrp, void *entry, void *__restrict user_arg, bool returns_int);
+int thread_attr_init(struct __mlibc_threadattr *attr);
+int thread_join(struct __mlibc_thread_data *thread, void *res);
+
+int thread_mutex_init(struct __mlibc_mutex *__restrict mutex, const struct __mlibc_mutexattr *__restrict attr);
+int thread_mutex_destroy(struct __mlibc_mutex *mutex);
+int thread_mutex_lock(struct __mlibc_mutex *mutex);
+int thread_mutex_unlock(struct __mlibc_mutex *mutex);
+
+int thread_mutexattr_init(struct __mlibc_mutexattr *attr);
+int thread_mutexattr_destroy(struct __mlibc_mutexattr *attr);
+int thread_mutexattr_gettype(const struct __mlibc_mutexattr *__restrict attr, int *__restrict type);
+int thread_mutexattr_settype(struct __mlibc_mutexattr *attr, int type);
+
+int thread_cond_init(struct __mlibc_cond *__restrict cond, const struct __mlibc_condattr *__restrict attr);
+int thread_cond_destroy(struct __mlibc_cond *cond);
+int thread_cond_broadcast(struct __mlibc_cond *cond);
+int thread_cond_timedwait(struct __mlibc_cond *__restrict cond, __mlibc_mutex *__restrict mutex, const struct timespec *__restrict abstime);
+
+}
diff --git a/lib/mlibc/options/internal/include/mlibc/tid.hpp b/lib/mlibc/options/internal/include/mlibc/tid.hpp
new file mode 100644
index 0000000..e85c19f
--- /dev/null
+++ b/lib/mlibc/options/internal/include/mlibc/tid.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <mlibc/thread.hpp>
+#include <mlibc/internal-sysdeps.hpp>
+
+namespace mlibc {
+ inline unsigned int this_tid() {
+ // During RTDL initialization, we don't have a TCB.
+ if (mlibc::tcb_available_flag) {
+ auto tcb = get_current_tcb();
+ return tcb->tid;
+ } else if (mlibc::sys_futex_tid) {
+ return mlibc::sys_futex_tid();
+ } else {
+ return 1;
+ }
+ }
+}
diff --git a/lib/mlibc/options/internal/include/stdint.h b/lib/mlibc/options/internal/include/stdint.h
new file mode 100644
index 0000000..4d8df66
--- /dev/null
+++ b/lib/mlibc/options/internal/include/stdint.h
@@ -0,0 +1,150 @@
+#ifndef _MLIBC_STDINT_H
+#define _MLIBC_STDINT_H
+
+#include <bits/types.h>
+#include <bits/wchar.h>
+
+// ----------------------------------------------------------------------------
+// Type definitions.
+// ----------------------------------------------------------------------------
+
+// Fixed-width (signed).
+typedef __mlibc_int8 int8_t;
+typedef __mlibc_int16 int16_t;
+typedef __mlibc_int32 int32_t;
+typedef __mlibc_int64 int64_t;
+
+// Fixed-width (unsigned).
+typedef __mlibc_uint8 uint8_t;
+typedef __mlibc_uint16 uint16_t;
+typedef __mlibc_uint32 uint32_t;
+typedef __mlibc_uint64 uint64_t;
+
+// Least-width (signed).
+typedef __mlibc_int8 int_least8_t;
+typedef __mlibc_int16 int_least16_t;
+typedef __mlibc_int32 int_least32_t;
+typedef __mlibc_int64 int_least64_t;
+
+// Least-width (unsigned).
+typedef __mlibc_uint8 uint_least8_t;
+typedef __mlibc_uint16 uint_least16_t;
+typedef __mlibc_uint32 uint_least32_t;
+typedef __mlibc_uint64 uint_least64_t;
+
+// Fast-width (signed).
+typedef __mlibc_int_fast8 int_fast8_t;
+typedef __mlibc_int_fast16 int_fast16_t;
+typedef __mlibc_int_fast32 int_fast32_t;
+typedef __mlibc_int_fast64 int_fast64_t;
+
+// Fast-width (unsigned).
+typedef __mlibc_uint_fast8 uint_fast8_t;
+typedef __mlibc_uint_fast16 uint_fast16_t;
+typedef __mlibc_uint_fast32 uint_fast32_t;
+typedef __mlibc_uint_fast64 uint_fast64_t;
+
+// Miscellaneous (signed).
+typedef __mlibc_intmax intmax_t;
+typedef __mlibc_intptr intptr_t;
+
+// Miscellaneous (unsigned).
+typedef __mlibc_uintmax uintmax_t;
+typedef __mlibc_uintptr uintptr_t;
+
+// ----------------------------------------------------------------------------
+// Constants.
+// ----------------------------------------------------------------------------
+
+// Fixed-width (signed).
+#define INT8_C(x) __MLIBC_INT8_C(x)
+#define INT16_C(x) __MLIBC_INT16_C(x)
+#define INT32_C(x) __MLIBC_INT32_C(x)
+#define INT64_C(x) __MLIBC_INT64_C(x)
+#define INTMAX_C(x) __MLIBC_INTMAX_C(x)
+
+// Fixed-width (unsigned).
+#define UINT8_C(x) __MLIBC_UINT8_C(x)
+#define UINT16_C(x) __MLIBC_UINT16_C(x)
+#define UINT32_C(x) __MLIBC_UINT32_C(x)
+#define UINT64_C(x) __MLIBC_UINT64_C(x)
+#define UINTMAX_C(x) __MLIBC_UINTMAX_C(x)
+
+// ----------------------------------------------------------------------------
+// Limits.
+// ----------------------------------------------------------------------------
+
+// Fixed-width (signed).
+#define INT8_MAX __MLIBC_INT8_MAX
+#define INT16_MAX __MLIBC_INT16_MAX
+#define INT32_MAX __MLIBC_INT32_MAX
+#define INT64_MAX __MLIBC_INT64_MAX
+
+#define INT8_MIN __MLIBC_INT8_MIN
+#define INT16_MIN __MLIBC_INT16_MIN
+#define INT32_MIN __MLIBC_INT32_MIN
+#define INT64_MIN __MLIBC_INT64_MIN
+
+// Fixed-width (unsigned).
+#define UINT8_MAX __MLIBC_UINT8_MAX
+#define UINT16_MAX __MLIBC_UINT16_MAX
+#define UINT32_MAX __MLIBC_UINT32_MAX
+#define UINT64_MAX __MLIBC_UINT64_MAX
+
+// Least-width (signed).
+#define INT_LEAST8_MAX __MLIBC_INT8_MAX
+#define INT_LEAST16_MAX __MLIBC_INT16_MAX
+#define INT_LEAST32_MAX __MLIBC_INT32_MAX
+#define INT_LEAST64_MAX __MLIBC_INT64_MAX
+
+#define INT_LEAST8_MIN __MLIBC_INT8_MIN
+#define INT_LEAST16_MIN __MLIBC_INT16_MIN
+#define INT_LEAST32_MIN __MLIBC_INT32_MIN
+#define INT_LEAST64_MIN __MLIBC_INT64_MIN
+
+// Least-width (unsigned).
+#define UINT_LEAST8_MAX __MLIBC_UINT8_MAX
+#define UINT_LEAST16_MAX __MLIBC_UINT16_MAX
+#define UINT_LEAST32_MAX __MLIBC_UINT32_MAX
+#define UINT_LEAST64_MAX __MLIBC_UINT64_MAX
+
+// Fast-width (signed).
+#define INT_FAST8_MAX __MLIBC_INT_FAST8_MAX
+#define INT_FAST16_MAX __MLIBC_INT_FAST16_MAX
+#define INT_FAST32_MAX __MLIBC_INT_FAST32_MAX
+#define INT_FAST64_MAX __MLIBC_INT_FAST64_MAX
+
+#define INT_FAST8_MIN __MLIBC_INT_FAST8_MIN
+#define INT_FAST16_MIN __MLIBC_INT_FAST16_MIN
+#define INT_FAST32_MIN __MLIBC_INT_FAST32_MIN
+#define INT_FAST64_MIN __MLIBC_INT_FAST64_MIN
+
+// Fast-width (unsigned).
+#define UINT_FAST8_MAX __MLIBC_UINT_FAST8_MAX
+#define UINT_FAST16_MAX __MLIBC_UINT_FAST16_MAX
+#define UINT_FAST32_MAX __MLIBC_UINT_FAST32_MAX
+#define UINT_FAST64_MAX __MLIBC_UINT_FAST64_MAX
+
+// Miscellaneous (signed).
+#define INTMAX_MAX __MLIBC_INTMAX_MAX
+#define INTPTR_MAX __MLIBC_INTPTR_MAX
+
+#define INTMAX_MIN __MLIBC_INTMAX_MIN
+#define INTPTR_MIN __MLIBC_INTPTR_MIN
+
+// Miscellaneous (unsigned).
+#define UINTMAX_MAX __MLIBC_UINTMAX_MAX
+#define UINTPTR_MAX __MLIBC_UINTPTR_MAX
+
+// Other limits (signed).
+#define PTRDIFF_MAX __MLIBC_PTRDIFF_MAX
+#define PTRDIFF_MIN __MLIBC_PTRDIFF_MIN
+#define SIG_ATOMIC_MAX __MLIBC_SIG_ATOMIC_MAX
+#define SIG_ATOMIC_MIN __MLIBC_SIG_ATOMIC_MIN
+#define WINT_MAX __MLIBC_WINT_MAX
+#define WINT_MIN __MLIBC_WINT_MIN
+
+// Other limits (unsigned).
+#define SIZE_MAX __MLIBC_SIZE_MAX
+
+#endif // _MLIBC_STDINT_H
diff --git a/lib/mlibc/options/internal/riscv64-include/mlibc/arch-defs.hpp b/lib/mlibc/options/internal/riscv64-include/mlibc/arch-defs.hpp
new file mode 100644
index 0000000..0a4789f
--- /dev/null
+++ b/lib/mlibc/options/internal/riscv64-include/mlibc/arch-defs.hpp
@@ -0,0 +1,12 @@
+#ifndef MLIBC_ARCH_DEFS_HPP
+#define MLIBC_ARCH_DEFS_HPP
+
+#include <stddef.h>
+
+namespace mlibc {
+
+inline constexpr size_t page_size = 0x1000;
+
+} // namespace mlibc
+
+#endif // MLIBC_ARCH_DEFS_HPP
diff --git a/lib/mlibc/options/internal/riscv64-include/mlibc/thread.hpp b/lib/mlibc/options/internal/riscv64-include/mlibc/thread.hpp
new file mode 100644
index 0000000..7428b75
--- /dev/null
+++ b/lib/mlibc/options/internal/riscv64-include/mlibc/thread.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <stdint.h>
+#include <mlibc/tcb.hpp>
+#include <bits/ensure.h>
+
+namespace mlibc {
+
+inline Tcb *get_current_tcb() {
+ // On RISC-V, the TCB is below the thread pointer.
+ uintptr_t tp = (uintptr_t)__builtin_thread_pointer();
+ auto tcb = reinterpret_cast<Tcb *>(tp - sizeof(Tcb));
+ __ensure(tcb == tcb->selfPointer);
+ return tcb;
+}
+
+inline uintptr_t get_sp() {
+ uintptr_t sp;
+ asm ("mv %0, sp" : "=r"(sp));
+ return sp;
+}
+
+} // namespace mlibc
diff --git a/lib/mlibc/options/internal/riscv64/fenv.S b/lib/mlibc/options/internal/riscv64/fenv.S
new file mode 100644
index 0000000..c62ea36
--- /dev/null
+++ b/lib/mlibc/options/internal/riscv64/fenv.S
@@ -0,0 +1,57 @@
+
+#ifdef __riscv_flen
+
+.global feclearexcept
+.type feclearexcept, %function
+feclearexcept:
+ csrc fflags, a0
+ li a0, 0
+ ret
+
+.global feraiseexcept
+.type feraiseexcept, %function
+feraiseexcept:
+ csrs fflags, a0
+ li a0, 0
+ ret
+
+.global fetestexcept
+.type fetestexcept, %function
+fetestexcept:
+ frflags t0
+ and a0, t0, a0
+ ret
+
+.global fegetround
+.type fegetround, %function
+fegetround:
+ frrm a0
+ ret
+
+.global __fesetround
+.type __fesetround, %function
+__fesetround:
+ fsrm t0, a0
+ li a0, 0
+ ret
+
+.global fegetenv
+.type fegetenv, %function
+fegetenv:
+ frcsr t0
+ sw t0, 0(a0)
+ li a0, 0
+ ret
+
+.global fesetenv
+.type fesetenv, %function
+fesetenv:
+ li t2, -1
+ li t1, 0
+ beq a0, t2, 1f
+ lw t1, 0(a0)
+1: fscsr t1
+ li a0, 0
+ ret
+
+#endif \ No newline at end of file
diff --git a/lib/mlibc/options/internal/riscv64/mlibc_crtbegin.S b/lib/mlibc/options/internal/riscv64/mlibc_crtbegin.S
new file mode 100644
index 0000000..b99748b
--- /dev/null
+++ b/lib/mlibc/options/internal/riscv64/mlibc_crtbegin.S
@@ -0,0 +1,29 @@
+
+.section .data
+.hidden __dso_handle
+.global __dso_handle
+__dso_handle:
+ .quad __dso_handle
+
+.section .init
+.hidden _init
+.global _init
+_init:
+
+.section .fini
+.hidden _fini
+.global _fini
+_fini:
+
+.section .ctors
+.hidden __CTOR_LIST__
+.global __CTOR_LIST__
+__CTOR_LIST__:
+
+.section .dtors
+.hidden __DTOR_LIST__
+.global __DTOR_LIST__
+__DTOR_LIST__:
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/options/internal/riscv64/mlibc_crtend.S b/lib/mlibc/options/internal/riscv64/mlibc_crtend.S
new file mode 100644
index 0000000..bd0cebc
--- /dev/null
+++ b/lib/mlibc/options/internal/riscv64/mlibc_crtend.S
@@ -0,0 +1,21 @@
+.hidden __mlibc_do_ctors
+.hidden __mlibc_do_dtors
+
+.section .init
+ tail __mlibc_do_ctors
+
+.section .fini
+ tail __mlibc_do_dtors
+
+.section .ctors
+.hidden __CTOR_END__
+.global __CTOR_END__
+__CTOR_END__:
+
+.section .dtors
+.hidden __DTOR_END__
+.global __DTOR_END__
+__DTOR_END__:
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/options/internal/riscv64/setjmp.S b/lib/mlibc/options/internal/riscv64/setjmp.S
new file mode 100644
index 0000000..51568f7
--- /dev/null
+++ b/lib/mlibc/options/internal/riscv64/setjmp.S
@@ -0,0 +1,71 @@
+.global setjmp
+.type setjmp, "function"
+setjmp:
+ sd ra, 0(a0)
+ sd s0, 8(a0)
+ sd s1, 16(a0)
+ sd s2, 24(a0)
+ sd s3, 32(a0)
+ sd s4, 40(a0)
+ sd s5, 48(a0)
+ sd s6, 56(a0)
+ sd s7, 64(a0)
+ sd s8, 72(a0)
+ sd s9, 80(a0)
+ sd s10, 88(a0)
+ sd s11, 96(a0)
+ sd sp, 104(a0)
+ fsd fs0, 112(a0)
+ fsd fs1, 120(a0)
+ fsd fs2, 128(a0)
+ fsd fs3, 136(a0)
+ fsd fs4, 144(a0)
+ fsd fs5, 152(a0)
+ fsd fs6, 160(a0)
+ fsd fs7, 168(a0)
+ fsd fs8, 176(a0)
+ fsd fs9, 184(a0)
+ fsd fs10, 192(a0)
+ fsd fs11, 200(a0)
+ li a0, 0
+ ret
+
+.global sigsetjmp
+.type sigsetjmp, "function"
+sigsetjmp:
+ unimp // TODO
+
+.global longjmp
+.type longjmp, "function"
+longjmp:
+ ld ra,0(a0)
+ ld s0,8(a0)
+ ld s1,16(a0)
+ ld s2,24(a0)
+ ld s3,32(a0)
+ ld s4,40(a0)
+ ld s5,48(a0)
+ ld s6,56(a0)
+ ld s7,64(a0)
+ ld s8,72(a0)
+ ld s9,80(a0)
+ ld s10,88(a0)
+ ld s11,96(a0)
+ ld sp,104(a0)
+ fld fs0,112(a0)
+ fld fs1,120(a0)
+ fld fs2,128(a0)
+ fld fs3,136(a0)
+ fld fs4,144(a0)
+ fld fs5,152(a0)
+ fld fs6,160(a0)
+ fld fs7,168(a0)
+ fld fs8,176(a0)
+ fld fs9,184(a0)
+ fld fs10,192(a0)
+ fld fs11,200(a0)
+ seqz a0,a1
+ add a0,a0,a1
+ ret
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/options/internal/x86-include/mlibc/arch-defs.hpp b/lib/mlibc/options/internal/x86-include/mlibc/arch-defs.hpp
new file mode 100755
index 0000000..aa8fe38
--- /dev/null
+++ b/lib/mlibc/options/internal/x86-include/mlibc/arch-defs.hpp
@@ -0,0 +1,13 @@
+#ifndef MLIBC_ARCH_DEFS_HPP
+#define MLIBC_ARCH_DEFS_HPP
+
+#include <stddef.h>
+
+namespace mlibc {
+
+inline constexpr size_t page_size = 0x1000;
+
+} // namespace mlibc
+
+#endif // MLIBC_ARCH_DEFS_HPP
+
diff --git a/lib/mlibc/options/internal/x86-include/mlibc/thread.hpp b/lib/mlibc/options/internal/x86-include/mlibc/thread.hpp
new file mode 100755
index 0000000..3475fb4
--- /dev/null
+++ b/lib/mlibc/options/internal/x86-include/mlibc/thread.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <stdint.h>
+#include <mlibc/tcb.hpp>
+
+namespace mlibc {
+
+inline Tcb *get_current_tcb() {
+ uintptr_t ptr;
+ asm ("movl %%gs:0, %0" : "=r"(ptr));
+ return reinterpret_cast<Tcb *>(ptr);
+}
+
+inline uintptr_t get_sp() {
+ uintptr_t esp;
+ asm ("mov %%esp, %0" : "=r"(esp));
+ return esp;
+}
+
+} // namespace mlibc
+
diff --git a/lib/mlibc/options/internal/x86/fenv.S b/lib/mlibc/options/internal/x86/fenv.S
new file mode 100644
index 0000000..a46b5fa
--- /dev/null
+++ b/lib/mlibc/options/internal/x86/fenv.S
@@ -0,0 +1,168 @@
+# The functions below are taken from musl.
+
+.hidden __hwcap
+
+.global feclearexcept
+.type feclearexcept,@function
+feclearexcept:
+ mov 4(%esp),%ecx
+ and $0x3f,%ecx
+ fnstsw %ax
+ # consider sse fenv as well if the cpu has XMM capability
+ call 1f
+1: addl $__hwcap-1b,(%esp)
+ pop %edx
+ testl $0x02000000,(%edx)
+ jz 2f
+ # maintain exceptions in the sse mxcsr, clear x87 exceptions
+ test %eax,%ecx
+ jz 1f
+ fnclex
+1: push %edx
+ stmxcsr (%esp)
+ pop %edx
+ and $0x3f,%eax
+ or %eax,%edx
+ test %edx,%ecx
+ jz 1f
+ not %ecx
+ and %ecx,%edx
+ push %edx
+ ldmxcsr (%esp)
+ pop %edx
+1: xor %eax,%eax
+ ret
+ # only do the expensive x87 fenv load/store when needed
+2: test %eax,%ecx
+ jz 1b
+ not %ecx
+ and %ecx,%eax
+ test $0x3f,%eax
+ jz 1f
+ fnclex
+ jmp 1b
+1: sub $32,%esp
+ fnstenv (%esp)
+ mov %al,4(%esp)
+ fldenv (%esp)
+ add $32,%esp
+ xor %eax,%eax
+ ret
+
+.global feraiseexcept
+.type feraiseexcept,@function
+feraiseexcept:
+ mov 4(%esp),%eax
+ and $0x3f,%eax
+ sub $32,%esp
+ fnstenv (%esp)
+ or %al,4(%esp)
+ fldenv (%esp)
+ add $32,%esp
+ xor %eax,%eax
+ ret
+
+.global __fesetround
+.hidden __fesetround
+.type __fesetround,@function
+__fesetround:
+ mov 4(%esp),%ecx
+ push %eax
+ xor %eax,%eax
+ fnstcw (%esp)
+ andb $0xf3,1(%esp)
+ or %ch,1(%esp)
+ fldcw (%esp)
+ # consider sse fenv as well if the cpu has XMM capability
+ call 1f
+1: addl $__hwcap-1b,(%esp)
+ pop %edx
+ testl $0x02000000,(%edx)
+ jz 1f
+ stmxcsr (%esp)
+ shl $3,%ch
+ andb $0x9f,1(%esp)
+ or %ch,1(%esp)
+ ldmxcsr (%esp)
+1: pop %ecx
+ ret
+
+.global fegetround
+.type fegetround,@function
+fegetround:
+ push %eax
+ fnstcw (%esp)
+ pop %eax
+ and $0xc00,%eax
+ ret
+
+.global fegetenv
+.type fegetenv,@function
+fegetenv:
+ mov 4(%esp),%ecx
+ xor %eax,%eax
+ fnstenv (%ecx)
+ # consider sse fenv as well if the cpu has XMM capability
+ call 1f
+1: addl $__hwcap-1b,(%esp)
+ pop %edx
+ testl $0x02000000,(%edx)
+ jz 1f
+ push %eax
+ stmxcsr (%esp)
+ pop %edx
+ and $0x3f,%edx
+ or %edx,4(%ecx)
+1: ret
+
+.global fesetenv
+.type fesetenv,@function
+fesetenv:
+ mov 4(%esp),%ecx
+ xor %eax,%eax
+ inc %ecx
+ jz 1f
+ fldenv -1(%ecx)
+ movl -1(%ecx),%ecx
+ jmp 2f
+1: push %eax
+ push %eax
+ push %eax
+ push %eax
+ pushl $0xffff
+ push %eax
+ pushl $0x37f
+ fldenv (%esp)
+ add $28,%esp
+ # consider sse fenv as well if the cpu has XMM capability
+2: call 1f
+1: addl $__hwcap-1b,(%esp)
+ pop %edx
+ testl $0x02000000,(%edx)
+ jz 1f
+ # mxcsr := same rounding mode, cleared exceptions, default mask
+ and $0xc00,%ecx
+ shl $3,%ecx
+ or $0x1f80,%ecx
+ mov %ecx,4(%esp)
+ ldmxcsr 4(%esp)
+1: ret
+
+.global fetestexcept
+.type fetestexcept,@function
+fetestexcept:
+ mov 4(%esp),%ecx
+ and $0x3f,%ecx
+ fnstsw %ax
+ # consider sse fenv as well if the cpu has XMM capability
+ call 1f
+1: addl $__hwcap-1b,(%esp)
+ pop %edx
+ testl $0x02000000,(%edx)
+ jz 1f
+ stmxcsr 4(%esp)
+ or 4(%esp),%eax
+1: and %ecx,%eax
+ ret
+
+.section .note.GNU-stack,"",%progbits
diff --git a/lib/mlibc/options/internal/x86/mlibc_crtbegin.S b/lib/mlibc/options/internal/x86/mlibc_crtbegin.S
new file mode 100644
index 0000000..d317451
--- /dev/null
+++ b/lib/mlibc/options/internal/x86/mlibc_crtbegin.S
@@ -0,0 +1,29 @@
+
+.section .data
+.hidden __dso_handle
+.global __dso_handle
+__dso_handle:
+ .long __dso_handle
+
+.section .init
+.hidden _init
+.global _init
+_init:
+
+.section .fini
+.hidden _fini
+.global _fini
+_fini:
+
+.section .ctors
+.hidden __CTOR_LIST__
+.global __CTOR_LIST__
+__CTOR_LIST__:
+
+.section .dtors
+.hidden __DTOR_LIST__
+.global __DTOR_LIST__
+__DTOR_LIST__:
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/options/internal/x86/mlibc_crtend.S b/lib/mlibc/options/internal/x86/mlibc_crtend.S
new file mode 100644
index 0000000..e9d9136
--- /dev/null
+++ b/lib/mlibc/options/internal/x86/mlibc_crtend.S
@@ -0,0 +1,24 @@
+
+.hidden __mlibc_do_ctors
+.hidden __mlibc_do_dtors
+
+.section .init
+ call __mlibc_do_ctors
+ ret
+
+.section .fini
+ call __mlibc_do_dtors
+ ret
+
+.section .ctors
+.hidden __CTOR_END__
+.global __CTOR_END__
+__CTOR_END__:
+
+.section .dtors
+.hidden __DTOR_END__
+.global __DTOR_END__
+__DTOR_END__:
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/options/internal/x86/setjmp.S b/lib/mlibc/options/internal/x86/setjmp.S
new file mode 100644
index 0000000..fa6644c
--- /dev/null
+++ b/lib/mlibc/options/internal/x86/setjmp.S
@@ -0,0 +1,53 @@
+
+.type __setjmp, "function"
+__setjmp:
+ mov 4(%esp), %eax # Save argument (buffer) in edi
+ mov %ebx, 0x00(%eax)
+ mov %ebp, 0x04(%eax)
+ mov %esi, 0x08(%eax)
+ mov %edi, 0x0c(%eax)
+
+ lea 4(%esp), %ecx # esp before return eip is pushed
+ mov %ecx, 0x10(%eax)
+ mov (%esp), %ecx # Return eip
+ mov %ecx, 0x14(%eax)
+
+ test %edx, %edx
+ jnz 1f
+ xor %eax, %eax
+ ret
+
+1:
+ jmp __sigsetjmp@PLT
+
+.global setjmp
+.type setjmp, "function"
+setjmp:
+ xor %edx, %edx
+ jmp __setjmp
+
+.global sigsetjmp
+.type sigsetjmp, "function"
+sigsetjmp:
+ mov $1, %edx
+ jmp __setjmp
+
+.global longjmp
+.type longjmp, "function"
+longjmp:
+ mov 4(%esp), %ecx
+ mov 0x00(%ecx), %ebx
+ mov 0x04(%ecx), %ebp
+ mov 0x08(%ecx), %esi
+ mov 0x0c(%ecx), %edi
+
+ mov 8(%esp), %eax
+ test %eax, %eax
+ jnz 1f
+ inc %eax
+1:
+ mov 0x10(%ecx), %esp
+ jmp *0x14(%ecx)
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/options/internal/x86_64-include/mlibc/arch-defs.hpp b/lib/mlibc/options/internal/x86_64-include/mlibc/arch-defs.hpp
new file mode 100644
index 0000000..0a4789f
--- /dev/null
+++ b/lib/mlibc/options/internal/x86_64-include/mlibc/arch-defs.hpp
@@ -0,0 +1,12 @@
+#ifndef MLIBC_ARCH_DEFS_HPP
+#define MLIBC_ARCH_DEFS_HPP
+
+#include <stddef.h>
+
+namespace mlibc {
+
+inline constexpr size_t page_size = 0x1000;
+
+} // namespace mlibc
+
+#endif // MLIBC_ARCH_DEFS_HPP
diff --git a/lib/mlibc/options/internal/x86_64-include/mlibc/thread.hpp b/lib/mlibc/options/internal/x86_64-include/mlibc/thread.hpp
new file mode 100644
index 0000000..ed02b67
--- /dev/null
+++ b/lib/mlibc/options/internal/x86_64-include/mlibc/thread.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <stdint.h>
+#include <mlibc/tcb.hpp>
+
+namespace mlibc {
+
+inline Tcb *get_current_tcb() {
+ uintptr_t ptr;
+ asm ("movq %%fs:0, %0" : "=r"(ptr));
+ return reinterpret_cast<Tcb *>(ptr);
+}
+
+inline uintptr_t get_sp() {
+ uintptr_t rsp;
+ asm ("mov %%rsp, %0" : "=r"(rsp));
+ return rsp;
+}
+
+} // namespace mlibc
diff --git a/lib/mlibc/options/internal/x86_64/fenv.S b/lib/mlibc/options/internal/x86_64/fenv.S
new file mode 100644
index 0000000..3748988
--- /dev/null
+++ b/lib/mlibc/options/internal/x86_64/fenv.S
@@ -0,0 +1,102 @@
+# The functions below are taken from musl.
+.global feclearexcept
+.type feclearexcept,@function
+feclearexcept:
+ # maintain exceptions in the sse mxcsr, clear x87 exceptions
+ mov %edi,%ecx
+ and $0x3f,%ecx
+ fnstsw %ax
+ test %eax,%ecx
+ jz 1f
+ fnclex
+1: stmxcsr -8(%rsp)
+ and $0x3f,%eax
+ or %eax,-8(%rsp)
+ test %ecx,-8(%rsp)
+ jz 1f
+ not %ecx
+ and %ecx,-8(%rsp)
+ ldmxcsr -8(%rsp)
+1: xor %eax,%eax
+ ret
+
+.global feraiseexcept
+.type feraiseexcept,@function
+feraiseexcept:
+ and $0x3f,%edi
+ stmxcsr -8(%rsp)
+ or %edi,-8(%rsp)
+ ldmxcsr -8(%rsp)
+ xor %eax,%eax
+ ret
+
+.global __fesetround
+.hidden __fesetround
+.type __fesetround,@function
+__fesetround:
+ push %rax
+ xor %eax,%eax
+ mov %edi,%ecx
+ fnstcw (%rsp)
+ andb $0xf3,1(%rsp)
+ or %ch,1(%rsp)
+ fldcw (%rsp)
+ stmxcsr (%rsp)
+ shl $3,%ch
+ andb $0x9f,1(%rsp)
+ or %ch,1(%rsp)
+ ldmxcsr (%rsp)
+ pop %rcx
+ ret
+
+.global fegetround
+.type fegetround,@function
+fegetround:
+ push %rax
+ stmxcsr (%rsp)
+ pop %rax
+ shr $3,%eax
+ and $0xc00,%eax
+ ret
+
+.global fegetenv
+.type fegetenv,@function
+fegetenv:
+ xor %eax,%eax
+ fnstenv (%rdi)
+ stmxcsr 28(%rdi)
+ ret
+
+.global fesetenv
+.type fesetenv,@function
+fesetenv:
+ xor %eax,%eax
+ inc %rdi
+ jz 1f
+ fldenv -1(%rdi)
+ ldmxcsr 27(%rdi)
+ ret
+1: push %rax
+ push %rax
+ pushq $0xffff
+ pushq $0x37f
+ fldenv (%rsp)
+ pushq $0x1f80
+ ldmxcsr (%rsp)
+ add $40,%rsp
+ ret
+
+.global fetestexcept
+.type fetestexcept,@function
+fetestexcept:
+ and $0x3f,%edi
+ push %rax
+ stmxcsr (%rsp)
+ pop %rsi
+ fnstsw %ax
+ or %esi,%eax
+ and %edi,%eax
+ ret
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/options/internal/x86_64/mlibc_crtbegin.S b/lib/mlibc/options/internal/x86_64/mlibc_crtbegin.S
new file mode 100644
index 0000000..b99748b
--- /dev/null
+++ b/lib/mlibc/options/internal/x86_64/mlibc_crtbegin.S
@@ -0,0 +1,29 @@
+
+.section .data
+.hidden __dso_handle
+.global __dso_handle
+__dso_handle:
+ .quad __dso_handle
+
+.section .init
+.hidden _init
+.global _init
+_init:
+
+.section .fini
+.hidden _fini
+.global _fini
+_fini:
+
+.section .ctors
+.hidden __CTOR_LIST__
+.global __CTOR_LIST__
+__CTOR_LIST__:
+
+.section .dtors
+.hidden __DTOR_LIST__
+.global __DTOR_LIST__
+__DTOR_LIST__:
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/options/internal/x86_64/mlibc_crtend.S b/lib/mlibc/options/internal/x86_64/mlibc_crtend.S
new file mode 100644
index 0000000..e9d9136
--- /dev/null
+++ b/lib/mlibc/options/internal/x86_64/mlibc_crtend.S
@@ -0,0 +1,24 @@
+
+.hidden __mlibc_do_ctors
+.hidden __mlibc_do_dtors
+
+.section .init
+ call __mlibc_do_ctors
+ ret
+
+.section .fini
+ call __mlibc_do_dtors
+ ret
+
+.section .ctors
+.hidden __CTOR_END__
+.global __CTOR_END__
+__CTOR_END__:
+
+.section .dtors
+.hidden __DTOR_END__
+.global __DTOR_END__
+__DTOR_END__:
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/options/internal/x86_64/setjmp.S b/lib/mlibc/options/internal/x86_64/setjmp.S
new file mode 100644
index 0000000..aa8a134
--- /dev/null
+++ b/lib/mlibc/options/internal/x86_64/setjmp.S
@@ -0,0 +1,54 @@
+
+.type __setjmp, "function"
+__setjmp:
+ mov %rbx, 0x00(%rdi)
+ mov %rbp, 0x08(%rdi)
+ mov %r12, 0x10(%rdi)
+ mov %r13, 0x18(%rdi)
+ mov %r14, 0x20(%rdi)
+ mov %r15, 0x28(%rdi)
+
+ lea 8(%rsp), %rax # rsp before return rip is pushed
+ mov %rax, 0x30(%rdi)
+ mov (%rsp), %rax # return rip
+ mov %rax, 0x38(%rdi)
+
+ test %rdx, %rdx
+ jnz 1f
+ xor %rax, %rax
+ ret
+
+1:
+ jmp __sigsetjmp
+
+.global setjmp
+.type setjmp, "function"
+setjmp:
+ xor %rdx, %rdx
+ jmp __setjmp
+
+.global sigsetjmp
+.type sigsetjmp, "function"
+sigsetjmp:
+ mov $1, %rdx
+ jmp __setjmp
+
+.global longjmp
+.type longjmp, "function"
+longjmp:
+ mov 0x00(%rdi), %rbx
+ mov 0x08(%rdi), %rbp
+ mov 0x10(%rdi), %r12
+ mov 0x18(%rdi), %r13
+ mov 0x20(%rdi), %r14
+ mov 0x28(%rdi), %r15
+
+ mov %rsi, %rax
+ test %rax, %rax
+ jnz 1f
+ inc %rax
+1:
+ mov 0x30(%rdi), %rsp
+ jmp *0x38(%rdi)
+.section .note.GNU-stack,"",%progbits
+