summaryrefslogtreecommitdiff
path: root/lib/mlibc/options/internal/include/mlibc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mlibc/options/internal/include/mlibc')
-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
19 files changed, 1141 insertions, 0 deletions
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;
+ }
+ }
+}