diff options
author | Ian Moffett <ian@osmora.org> | 2024-03-07 17:28:00 -0500 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2024-03-07 17:28:32 -0500 |
commit | bd5969fc876a10b18613302db7087ef3c40f18e1 (patch) | |
tree | 7c2b8619afe902abf99570df2873fbdf40a4d1a1 /lib/mlibc/options/internal | |
parent | a95b38b1b92b172e6cc4e8e56a88a30cc65907b0 (diff) |
lib: Add mlibc
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'lib/mlibc/options/internal')
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 + |