diff options
Diffstat (limited to 'lib/mlibc/options/internal/include/mlibc')
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; + } + } +} |