user: implement mlibc as the libc, finally.

It's finally done..

Signed-off-by: kaguya <vpshinomiya@protonmail.com>
This commit is contained in:
kaguya
2026-05-02 03:31:49 -04:00
parent 2fa39ad85a
commit 9a9b91c940
2387 changed files with 152741 additions and 315 deletions
@@ -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
@@ -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 volatile ("mrs %0, tpidr_el0" : "=r"(ptr));
return reinterpret_cast<Tcb *>(ptr + 0x10 - sizeof(Tcb));
}
inline uintptr_t get_sp() {
uintptr_t sp;
asm volatile ("mov %0, sp" : "=r"(sp));
return sp;
}
} // namespace mlibc
@@ -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
@@ -0,0 +1,67 @@
// vim: ft=arm64asm
.extern __sigsetjmp
.type __setjmp, "function"
__setjmp:
stp x19, x20, [x0, #0]
stp x21, x22, [x0, #16]
stp x23, x24, [x0, #32]
stp x25, x26, [x0, #48]
stp x27, x28, [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"
.global _setjmp
.type _setjmp, "function"
setjmp:
_setjmp:
mov x2, xzr
b __setjmp
.global sigsetjmp
.type sigsetjmp, "function"
sigsetjmp:
mov x2, #1
b __setjmp
.global longjmp
.type longjmp, "function"
.global _longjmp
.type _longjmp, "function"
longjmp:
_longjmp:
ldp x19, x20, [x0, #0]
ldp x21, x22, [x0, #16]
ldp x23, x24, [x0, #32]
ldp x25, x26, [x0, #48]
ldp x27, x28, [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
@@ -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.");
}
#endif
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.");
}
@@ -0,0 +1,70 @@
#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;
// padding to ensure correct alignment on certain platforms.
uint8_t padding[3];
// 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();
}
@@ -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();
};
@@ -0,0 +1,209 @@
#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;
}
size_t MemoryAllocator::get_size(void *ptr) {
if constexpr (logAllocations)
mlibc::infoLogger() << "MemoryAllocator::get_size(" << 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::get_size" << frg::endlog;
return meta->allocatedSize;
}
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 */
@@ -0,0 +1,268 @@
#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;
};
#define NSEQ_STORE(VAL) do { \
if (!static_cast<bool>(nseq)) { \
return charcode_error::output_overflow; \
} \
*nseq.it = (VAL); \
++nseq.it; \
} while (0)
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;
if (wc <= 0x7F) {
NSEQ_STORE(wc);
} else if (wc <= 0x7FF) {
NSEQ_STORE(0xC0 | (wc >> 6));
NSEQ_STORE(0x80 | (wc & 0x3f));
} else if (wc <= 0xFFFF) {
NSEQ_STORE(0xE0 | (wc >> 12));
NSEQ_STORE(0x80 | ((wc >> 6) & 0x3f));
NSEQ_STORE(0x80 | (wc & 0x3f));
} else if (wc <= 0x10FFFF) {
NSEQ_STORE(0xF0 | (wc >> 18));
NSEQ_STORE(0x80 | ((wc >> 12) & 0x3f));
NSEQ_STORE(0x80 | ((wc >> 6) & 0x3f));
NSEQ_STORE(0x80 | (wc & 0x3f));
} else {
return charcode_error::illegal_input;
}
++wseq.it;
return charcode_error::null;
}
};
#undef NSEQ_STORE
};
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
@@ -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
@@ -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
@@ -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;
}
@@ -0,0 +1,215 @@
#include <string.h>
#include <stdint.h>
namespace {
template<typename T>
[[gnu::always_inline]]
inline T alias_load(const unsigned char *&p) {
T value;
__builtin_memcpy(&value, p, sizeof(value));
p += sizeof(T);
return value;
}
template<typename T>
[[gnu::always_inline]]
inline void alias_store(unsigned char *&p, T value) {
__builtin_memcpy(p, &value, sizeof(value));
p += sizeof(T);
}
#if defined(__LP64__) && !defined(__riscv)
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__
} // namespace
// --------------------------------------------------------------------------------------
// 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 && (uintptr_t(curDest) & 7)) {
*curDest++ = byte;
--n;
}
auto pattern64 = static_cast<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<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<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) {
// Use uintptr_t for pointer comparisons because otherwise it's undefined behaviour
// when dest and src point to different objects.
uintptr_t udest = reinterpret_cast<uintptr_t>(dest);
uintptr_t usrc = reinterpret_cast<uintptr_t>(src);
if(udest < usrc || usrc + size <= udest) {
return forward_copy(dest, src, size);
} else if(udest > usrc) {
char *dest_bytes = (char *)dest;
char *src_bytes = (char *)src;
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;
}
@@ -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);
}
@@ -0,0 +1,393 @@
#include <assert.h>
#include <bits/ensure.h>
#include <bits/getopt.h>
#include <frg/optional.hpp>
#include <mlibc-config.h>
#include <mlibc/debug.hpp>
#include <mlibc/getopt.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <variant>
char *optarg;
int optind = 1;
int opterr = 1;
int optopt;
namespace {
int __optpos = 1;
int getopt_common_internal(int argc, char * const argv[], const char *optstring, const struct option *longopts,
int *longindex, enum mlibc::GetoptMode mode) {
// find a matching longopt for an `arg` of length `n`
// returns a size_t of the index of the matched longopt, preferring an exact over a partial match
// returns a char of the error that getopt should return if multiple matches were available
// returns a std::monostate if no longopt resulted in a exact or partial match
auto longopt_find = [&](const char *arg, size_t n) -> std::variant<size_t, char, std::monostate> {
assert(mode != mlibc::GetoptMode::Short);
frg::optional<size_t> i = frg::null_opt;
// first, attempt to find exactly one exact match
for(size_t longopt = 0; longopts[longopt].name; longopt++) {
if(strncmp(arg, longopts[longopt].name, n) || longopts[longopt].name[n])
continue;
if(i) {
if(opterr)
fprintf(stderr, "Multiple option declaration detected: %s\n", arg);
optind++;
return '?';
}
i = longopt;
}
if(i)
return *i;
// because no exact match was found, we now search for longopts with partial matches
for(size_t longopt = 0; longopts[longopt].name; longopt++) {
if(strncmp(arg, longopts[longopt].name, n))
continue;
if(i) {
if(opterr)
fprintf(stderr, "Multiple option declaration detected: %s\n", arg);
optind++;
return '?';
}
i = longopt;
}
if(i)
return *i;
return std::monostate{};
};
auto longopt_consume = [&](const char *arg, char *s, int k, bool colon) -> frg::optional<int> {
assert(mode != mlibc::GetoptMode::Short);
// Consume the option and its argument.
if(longopts[k].has_arg == required_argument) {
if(s) {
// Consume the long option and its argument.
optarg = s + 1;
optind++;
}else if(optind + 1 < argc && argv[optind + 1]) {
// Consume the long option.
optind++;
// Consume the option's argument.
optarg = argv[optind];
optind++;
}else{
/* If an error was detected, and the first character of optstring is not a colon,
and the external variable opterr is nonzero (which is the default),
getopt() prints an error message. */
if(!colon && opterr)
fprintf(stderr, "--%s requires an argument.\n", arg);
optopt = longopts[k].val;
optind++;
/* If the first character of optstring is a colon (':'), then getopt()
returns ':' instead of '?' to indicate a missing option argument. */
return colon ? ':' : '?';
}
}else if(longopts[k].has_arg == optional_argument) {
if(s) {
// Consume the long option and its argument.
optarg = s + 1;
optind++;
}else{
// Consume the long option.
optarg = nullptr;
optind++;
}
}else{
__ensure(longopts[k].has_arg == no_argument);
// did we get passed a value?
if(s && strlen(s)) {
optind++;
return colon ? ':' : '?';
}
// Consume the long option.
optind++;
optarg = nullptr;
}
return frg::null_opt;
};
bool colon = optstring[0] == ':';
bool stop_at_first_nonarg = (optstring[0] == '+' || getenv("POSIXLY_CORRECT"));
// if optstring contains "W;", then "-W foo" is treated as the long option "--foo".
bool w_long_options = [&]{
if(mode == mlibc::GetoptMode::Short)
return false;
const char *W = strchr(optstring, 'W');
if (!W)
return false;
return W[1] == ';';
}();
auto isOptionArg = [](char *arg){
// If the first character of arg '-', and the arg is not exactly
// equal to "-" or "--", then the arg is an option argument.
return arg[0] == '-' && strcmp(arg, "-") && strcmp(arg, "--");
};
while(optind < argc) {
char *arg = argv[optind];
if(!isOptionArg(arg)) {
if(!strcmp(arg, "--")) {
optind++;
return -1;
}
if(stop_at_first_nonarg) {
optarg = nullptr;
return -1;
}
bool further_options = false;
int skip = optind;
for(; skip < argc; ++skip) {
if(isOptionArg(argv[skip])) {
further_options = true;
break;
}
}
if(further_options) {
optind += skip - optind;
continue;
} else {
optarg = nullptr;
return -1;
}
}
if(arg[1] == '-' && mode != mlibc::GetoptMode::Short) {
arg += 2;
// Determine the end of the option name (vs. the start of the argument).
auto s = strchr(arg, '=');
size_t n = s ? (s - arg) : strlen(arg);
auto k = longopt_find(arg, n);
if(std::holds_alternative<std::monostate>(k)) {
if(opterr)
fprintf(stderr, "--%s is not a valid option.\n", arg);
optind++;
return '?';
} else if(std::holds_alternative<char>(k)) {
return std::get<char>(k);
}
if(longindex)
*longindex = std::get<size_t>(k);
if(auto r = longopt_consume(arg, s, std::get<size_t>(k), colon); r)
return r.value();
if(!longopts[std::get<size_t>(k)].flag) {
return longopts[std::get<size_t>(k)].val;
}else{
*longopts[std::get<size_t>(k)].flag = longopts[std::get<size_t>(k)].val;
return 0;
}
}else{
/* handle short options, i.e. options with only one dash prefixed; e.g. `program -s` */
unsigned int i = __optpos;
while(true) {
if(mode == mlibc::GetoptMode::LongOnly) {
const char *lo_arg = &arg[1];
auto s = strchr(lo_arg, '=');
size_t n = s ? (s - lo_arg) : strlen(lo_arg);
auto longopt_res = longopt_find(lo_arg, n);
if(std::holds_alternative<char>(longopt_res)) {
return std::get<char>(longopt_res);
} else if(std::holds_alternative<size_t>(longopt_res)) {
auto k = std::get<size_t>(longopt_res);
if(auto r = longopt_consume(lo_arg, s, k, colon); r)
return r.value();
if(!longopts[k].flag) {
return longopts[k].val;
}else{
*longopts[k].flag = longopts[k].val;
return 0;
}
}
}
auto opt = strchr(optstring, arg[i]);
if(opt) {
if(opt[0] == 'W' && w_long_options) {
const char *lo_arg = [&]() {
if(opt[1]) {
return &arg[i] + 1;
} else {
return &arg[i + 1];
}
}();
auto s = strchr(lo_arg, '=');
size_t n = s ? (s - lo_arg) : strlen(lo_arg);
if(!n) {
optopt = 'W';
optind++;
return colon ? ':' : '?';
}
auto longopt_res = longopt_find(lo_arg, n);
if(std::holds_alternative<char>(longopt_res)) {
return std::get<char>(longopt_res);
} else if(std::holds_alternative<size_t>(longopt_res)) {
auto k = std::get<size_t>(longopt_res);
if(auto r = longopt_consume(lo_arg, s, k, colon); r)
return r.value();
if(!longopts[k].flag) {
return longopts[k].val;
}else{
*longopts[k].flag = longopts[k].val;
return 0;
}
} else {
optind++;
return colon ? ':' : '?';
}
} else if(opt[1] == ':') {
// one colon means the option requires an argument
// two colons mean the option takes an optional argument as part of the
// same argv element (in the same word as the option name itself)
bool required = (opt[2] != ':');
if(arg[i+1]) {
optarg = arg + i + 1;
} else if(optind + 1 < argc && argv[optind + 1] && required && argv[optind + 1][0] != '-') {
/* there is an argument to this short option, separated by a space,
* and the shortopt specification does not specify an optional arg */
optarg = argv[optind + 1];
optind++;
__optpos = 1;
} else if(!required) {
optarg = nullptr;
} else {
__optpos = 1;
optopt = arg[i];
return colon ? ':' : '?';
}
optind++;
} else {
if(arg[i+1]) {
__optpos++;
} else if(arg[i]) {
optind++;
} else {
optarg = nullptr;
return -1;
}
}
return arg[i];
} else {
/* If getopt() does not recognize an option character, it prints an error message to stderr,
stores the character in optopt, and returns '?'. The calling program may prevent
the error message by setting opterr to 0. */
optopt = arg[1];
if(opterr)
fprintf(stderr, "%s is not a valid option.\n", arg);
optind++;
return '?';
}
}
}
}
optarg = nullptr;
return -1;
}
void permute(char **argv, int dest, int src) {
assert(src > dest);
char *tmp = argv[src];
for(int i = src; i > dest; i--) {
argv[i] = argv[i - 1];
}
argv[dest] = tmp;
}
} // namespace
#if __MLIBC_BSD_OPTION
extern "C" int optreset;
#endif /*__MLIBC_BSD_OPTION */
namespace mlibc {
int getopt_common(int argc, char * const argv[], const char *optstring,
const struct option *longopts, int *longindex, enum GetoptMode mode) {
// glibc extension: Setting optind to zero causes a full reset.
// TODO: Should we really reset opterr and the other flags?
if(!optind
#if __MLIBC_BSD_OPTION
|| optreset
#endif //__MLIBC_BSD_OPTION
) {
optarg = nullptr;
optind = 1;
optopt = 0;
__optpos = 1;
#if __MLIBC_BSD_OPTION
optreset = 0;
#endif //__MLIBC_BSD_OPTION
}
int skipped = optind;
if(optstring[0] != '+' && optstring[0] != '-') {
int i = optind;
for(;; i++) {
if(i >= argc || !argv[i]) {
optarg = nullptr;
return -1;
}
if(argv[i][0] == '-' && argv[i][1])
break;
}
optind = i;
}
int resumed = optind;
auto ret = getopt_common_internal(argc, argv, optstring, longopts, longindex, mode);
if(resumed > skipped) {
for(int i = 0; i < (optind - resumed); i++) {
permute(const_cast<char **>(argv), skipped, optind - 1);
}
optind = skipped + (optind - resumed);
}
return ret;
}
} // namespace mlibc
@@ -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");
}
} // namespace mlibc
@@ -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_RTLD
#include <math.h>
#endif
@@ -0,0 +1,99 @@
#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 if(item == T_FMT_AMPM) {
return const_cast<char *>("%I:%M:%S %p");
}else if(item == D_T_FMT) {
return const_cast<char *>("%a %b %e %T %Y");
} else if (item == RADIXCHAR) {
return const_cast<char *>(".");
} else if (item == THOUSEP) {
return const_cast<char *>("");
}else if(item == YESEXPR) {
return const_cast<char *>("^[yY]");
}else if(item == NOEXPR) {
return const_cast<char *>("^[nN]");
}else{
mlibc::infoLogger() << "mlibc: nl_langinfo item "
<< item << " is not implemented properly" << frg::endlog;
return const_cast<char *>("");
}
}
} // namespace mlibc
@@ -0,0 +1,98 @@
#include <mlibc/search.hpp>
#include <frg/string.hpp>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
struct _ENTRY {
ENTRY entry;
bool used;
};
namespace mlibc {
int hcreate_r(size_t num_entries, struct hsearch_data *htab) {
if(!htab) {
errno = EINVAL;
return 0;
}
htab->table = static_cast<_ENTRY*>(calloc(num_entries, sizeof(_ENTRY)));
if(!htab->table) {
errno = ENOMEM;
return 0;
}
htab->filled = 0;
htab->size = num_entries;
return 1;
}
void hdestroy_r(struct hsearch_data *htab) {
if(!htab) {
errno = EINVAL;
return;
}
free(htab->table);
htab->table = nullptr;
htab->size = 0;
htab->filled = 0;
}
int hsearch_r(ENTRY item, ACTION action, ENTRY **ret, struct hsearch_data *htab) {
auto key = frg::string_view{item.key};
auto hash = frg::hash<frg::string_view>{}(key);
size_t bucket_index = hash % htab->size;
size_t start = bucket_index;
while(true) {
auto &bucket = htab->table[bucket_index];
if(bucket.used) {
if(bucket.entry.key == key) {
*ret = &bucket.entry;
return 1;
}
} else if(action == FIND) {
errno = ESRCH;
*ret = nullptr;
return 0;
}
bucket_index = (bucket_index + 1) % htab->size;
if(bucket_index == start) {
if(action == FIND) {
errno = ESRCH;
*ret = nullptr;
return 0;
} else {
break;
}
}
}
// insert a new entry.
if(htab->size == htab->filled) {
errno = ENOMEM;
return 0;
}
++htab->filled;
bucket_index = start;
while(true) {
auto &bucket = htab->table[bucket_index];
if(!bucket.used) {
bucket.used = true;
bucket.entry = item;
*ret = &bucket.entry;
break;
}
bucket_index = (bucket_index + 1) % htab->size;
}
return 1;
}
} // namespace mlibc
@@ -0,0 +1,92 @@
#include <bits/sigset_t.h>
#include <bits/ensure.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <stddef.h>
namespace {
template<class T> struct remove_reference { typedef T type; };
template<class T> struct remove_reference<T&> { typedef T type; };
// Assume that the struct has a member named 'sig'.
template<typename T>
struct sigset_type_helper {
using type = typename remove_reference<decltype(T::sig[0])>::type;
static_assert(offsetof(T, sig) == 0);
};
template<>
struct sigset_type_helper<unsigned long> { using type = unsigned long; };
template<>
struct sigset_type_helper<unsigned long long> { using type = unsigned long long; };
template<>
struct sigset_type_helper<long> { using type = long; };
template<>
struct sigset_type_helper<long long> { using type = long long; };
// Some ABIs define sigset_t as a simple integer (e.g unsigned long),
// while others define it as a struct containing an array of integers.
using sigset_underlying_type = sigset_type_helper<sigset_t>::type;
size_t signo_to_field(int signo) {
return signo / (sizeof(sigset_underlying_type) * CHAR_BIT);
}
size_t signo_to_bit(int signo) {
return signo % (sizeof(sigset_underlying_type) * CHAR_BIT);
}
} // namespace
int sigemptyset(sigset_t *sigset) {
memset(sigset, 0, sizeof(*sigset));
return 0;
}
int sigfillset(sigset_t *sigset) {
memset(sigset, ~0, sizeof(*sigset));
return 0;
}
int sigaddset(sigset_t *sigset, int sig) {
int signo = sig - 1;
if(signo < 0 || static_cast<unsigned int>(signo) >= (sizeof(sigset_t) * CHAR_BIT)) {
errno = EINVAL;
return -1;
}
auto ptr = reinterpret_cast<sigset_underlying_type *>(sigset);
auto field = signo_to_field(signo);
auto bit = signo_to_bit(signo);
ptr[field] |= (1UL << bit);
return 0;
}
int sigdelset(sigset_t *sigset, int sig) {
int signo = sig - 1;
if(signo < 0 || static_cast<unsigned int>(signo) >= (sizeof(sigset_t) * CHAR_BIT)) {
errno = EINVAL;
return -1;
}
auto ptr = reinterpret_cast<sigset_underlying_type *>(sigset);
auto field = signo_to_field(signo);
auto bit = signo_to_bit(signo);
ptr[field] &= ~(1UL << bit);
return 0;
}
int sigismember(const sigset_t *sigset, int sig) {
int signo = sig - 1;
if(signo < 0 || static_cast<unsigned int>(signo) >= (sizeof(sigset_t) * CHAR_BIT)) {
errno = EINVAL;
return -1;
}
auto ptr = reinterpret_cast<const sigset_underlying_type *>(sigset);
auto field = signo_to_field(signo);
auto bit = signo_to_bit(signo);
return (ptr[field] & (1UL << bit)) != 0;
}
@@ -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;
}
} // namespace mlibc
@@ -0,0 +1,346 @@
#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 *__rtld_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 = __rtld_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;
new_tcb->isJoinable = (attr.__mlibc_detachstate == __MLIBC_THREAD_CREATE_JOINABLE);
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(!tcb->isJoinable) {
mlibc::infoLogger() << "mlibc: pthread_join() called on a detached thread" << frg::endlog;
return EINVAL;
}
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;
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.
// Also do so in case of a signal being caught.
if (e && e != EAGAIN && e != EINTR)
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
@@ -0,0 +1,282 @@
#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;
}
struct FloatCastOverflowData {
SourceLocation loc;
const TypeDescriptor &from_type;
const TypeDescriptor &to_type;
};
extern "C" [[gnu::visibility("hidden")]]
void __ubsan_handle_float_cast_overflow(FloatCastOverflowData *data, ValueHandle from) {
(void) from;
mlibc::panicLogger()
<< LOG_NAME_LOC("float cast overflow", data->loc)
<< "from " << data->from_type << " to "
<< data->to_type << frg::endlog;
}
struct FunctionTypeMismatchData {
SourceLocation loc;
const TypeDescriptor &type;
};
extern "C" [[gnu::visibility("hidden")]]
void __ubsan_handle_function_type_mismatch(FunctionTypeMismatchData *data, ValueHandle from) {
(void) from;
mlibc::panicLogger()
<< LOG_NAME_LOC("function type mismatch", data->loc)
<< frg::endlog;
}
@@ -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 */
@@ -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 */
@@ -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 */
@@ -0,0 +1,6 @@
#ifndef MLIBC_FILE_T_H
#define MLIBC_FILE_T_H
typedef struct __mlibc_file_base FILE;
#endif /* MLIBC_FILE_T_H */
@@ -0,0 +1,15 @@
#ifndef MLIBC_BITS_GETOPT
#define MLIBC_BITS_GETOPT
struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
#define no_argument 0
#define required_argument 1
#define optional_argument 2
#endif /* MLIBC_BITS_GETOPT */
@@ -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 */
@@ -0,0 +1,126 @@
#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;
};
#elif defined (__m68k__)
struct __mlibc_jmpbuf_register_state {
uint32_t d2;
uint32_t d3;
uint32_t d4;
uint32_t d5;
uint32_t d6;
uint32_t d7;
uint32_t a2;
uint32_t a3;
uint32_t a4;
uint32_t a5;
uint32_t a6;
uint32_t a7;
uint32_t sp;
uint32_t pc;
};
#elif defined (__loongarch64)
struct __mlibc_jmpbuf_register_state {
uint64_t ra;
uint64_t sp;
uint64_t u0;
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;
double fs0;
double fs1;
double fs2;
double fs3;
double fs4;
double fs5;
double fs6;
double fs7;
};
#else
# error "Missing architecture specific code"
#endif
#endif /* MLIBC_MACHINE_H */
@@ -0,0 +1,12 @@
#ifndef MLIBC_MBSTATE_H
#define MLIBC_MBSTATE_H
typedef struct __mlibc_mbstate {
short __progress;
short __shift;
unsigned int __cpoint;
} mbstate_t;
#define __MLIBC_MBSTATE_INITIALIZER {0, 0, 0}
#endif /* MLIBC_MBSTATE_H */
@@ -0,0 +1,84 @@
#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 DECIMAL_POINT RADIXCHAR
#define THOUSEP 0x50001
#define THOUSANDS_SEP THOUSEP
#define YESEXPR 0x70000
#define NOEXPR 0x70001
#ifdef __cplusplus
}
#endif
#endif /* _NL_ITEM_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 */
@@ -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 */
@@ -0,0 +1,24 @@
#ifndef _MLIBC_INTERNAL_SEARCH_H
#define _MLIBC_INTERNAL_SEARCH_H
#include <bits/size_t.h>
typedef enum {
FIND,
ENTER
} ACTION;
typedef struct entry {
char *key;
void *data;
} ENTRY;
struct _ENTRY;
struct hsearch_data {
struct _ENTRY *table;
unsigned int size;
unsigned int filled;
};
#endif /* _MLIBC_INTERNAL_SEARCH_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 *__sigset);
int sigfillset(sigset_t *__sigset);
int sigaddset(sigset_t *__sigset, int __sig);
int sigdelset(sigset_t *__sigset, int __sig);
int sigismember(const sigset_t *__sigset, int __sig);
#endif /* !__MLIBC_ABI_ONLY */
#ifdef __cplusplus
}
#endif
#endif /*MLIBC_BITS_SIGSET_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 */
@@ -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 */
@@ -0,0 +1,81 @@
#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
#define __MLIBC_THREAD_MUTEX_INITIALIZER {0, 0, 0, 0}
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 */
@@ -0,0 +1,408 @@
#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
#elif defined (__m68k__)
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_INT16_C(x)
#define __MLIBC_INT_FAST16_MAX __MLIBC_INT16_MAX
#define __MLIBC_INT_FAST16_MIN __MLIBC_INT16_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 (__loongarch64)
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
#elif defined (__m68k__)
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_UINT16_C(x)
#define __MLIBC_UINT_FAST16_MAX __MLIBC_UINT16_MAX
#define __MLIBC_UINT_FAST16_MIN __MLIBC_UINT16_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 (__loongarch64)
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(__cplusplus) && defined(__cpp_static_assert) && __cpp_static_assert >= 200410L
# define __MLIBC_STATIC_ASSERT(c, text) static_assert(c, text)
#elif !defined(__cplusplus)
/* _Static_assert is an extension in C89/C99. */
# define __MLIBC_STATIC_ASSERT(c, text) __extension__ _Static_assert(c, text)
#else
# define __MLIBC_STATIC_ASSERT(c, text) extern int __static_assert_unavailable
#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 */
@@ -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 */
@@ -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 */
@@ -0,0 +1,6 @@
#ifndef MLIBC_WCTRANS_T_H
#define MLIBC_WCTRANS_T_H
typedef unsigned long wctrans_t;
#endif /* MLIBC_WCTRANS_T_H */
@@ -0,0 +1,7 @@
#ifndef MLIBC_WCTYPE_T_H
#define MLIBC_WCTYPE_T_H
typedef unsigned long wctype_t;
#endif /* MLIBC_WCTYPE_T_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 */
@@ -0,0 +1,6 @@
#ifndef MLIBC_WINT_T_H
#define MLIBC_WINT_T_H
typedef __WINT_TYPE__ wint_t;
#endif /* MLIBC_WINT_T_H */
@@ -0,0 +1,127 @@
#pragma once
// DWARF expressions
#define DW_OP_deref 0x06
#define DW_OP_breg0 0x70
#define DW_CFA_def_cfa_expression 0x0f
#define DW_CFA_expression 0x10
#if defined(__x86_64__)
#define DWARF_REG_RAX 0
#define DWARF_REG_RDX 1
#define DWARF_REG_RCX 2
#define DWARF_REG_RBX 3
#define DWARF_REG_RSI 4
#define DWARF_REG_RDI 5
#define DWARF_REG_RBP 6
#define DWARF_REG_RSP 7
#define DWARF_REG_R8 8
#define DWARF_REG_R9 9
#define DWARF_REG_R10 10
#define DWARF_REG_R11 11
#define DWARF_REG_R12 12
#define DWARF_REG_R13 13
#define DWARF_REG_R14 14
#define DWARF_REG_R15 15
#define DWARF_REG_RETURN_ADDRESS 16
#define DWARF_REG_XMM0 17
#define DWARF_REG_XMM1 18
#define DWARF_REG_XMM2 19
#define DWARF_REG_XMM3 20
#define DWARF_REG_XMM4 21
#define DWARF_REG_XMM5 22
#define DWARF_REG_XMM6 23
#define DWARF_REG_XMM7 24
#define DWARF_REG_XMM8 25
#define DWARF_REG_XMM9 26
#define DWARF_REG_XMM10 27
#define DWARF_REG_XMM11 28
#define DWARF_REG_XMM12 29
#define DWARF_REG_XMM13 30
#define DWARF_REG_XMM14 31
#define DWARF_REG_XMM15 32
#define DWARF_REG_ST0 33
#define DWARF_REG_ST1 34
#define DWARF_REG_ST2 35
#define DWARF_REG_ST3 36
#define DWARF_REG_ST4 37
#define DWARF_REG_ST5 38
#define DWARF_REG_ST6 39
#define DWARF_REG_ST7 40
#define DWARF_REG_MM0 41
#define DWARF_REG_MM1 42
#define DWARF_REG_MM2 43
#define DWARF_REG_MM3 44
#define DWARF_REG_MM4 45
#define DWARF_REG_MM5 46
#define DWARF_REG_MM6 47
#define DWARF_REG_MM7 48
#define DWARF_REG_RFLAGS 49
#define DWARF_REG_ES 50
#define DWARF_REG_CS 51
#define DWARF_REG_SS 52
#define DWARF_REG_DS 53
#define DWARF_REG_FS 54
#define DWARF_REG_GS 55
#define DWARF_REG_TR 62
#define DWARF_REG_LDTR 63
#define DWARF_REG_MXCSR 64
#define DWARF_REG_FCW 65
#define DWARF_REG_FSW 66
#endif // defined(__x86_64__)
#if defined(__ASSEMBLER__)
#define DWARF_ULEB128_14BIT_SIZE(n) (1 + (((n) > 0x7f) & 1))
#define DWARF_SLEB128_14BIT_SIZE(n) (1 + (((n) < -0x40) & 1) + (((n) > 0x3f) & 1))
// write an up to 2-byte signed leb128 value
.macro cfi_emit_sleb128 val
.if (\val) < -0x2000 || (\val) > 0x1fff // doesn't fit in 2 bytes
.error "cfi_emit_sleb128 value is out of range (\val)"
.elseif (\val) < -0x40 || (\val) > 0x3f // doesn't fit in 1 byte
.cfi_escape ((\val) & 0x7f) | 0x80
.cfi_escape ((\val) >> 7) & 0x7f
.else // fits in 1 byte
.cfi_escape (\val) & 0x7f
.endif
.endm
// write an up to 2-byte unsigned leb128 value
.macro cfi_emit_uleb128 val
.if (\val) < 0 || (\val) > 0x3fff // doesn't fit in 2 bytes
.error "cfi_emit_uleb128 value is out of range (\val)"
.elseif (\val) > 0x7f // doesn't fit in 1 byte
.cfi_escape ((\val) & 0x7f) | 0x80
.cfi_escape (\val) >> 7
.else
.cfi_escape (\val)
.endif
.endm
.macro cfi_set_cfa_to_ptr_with_offset target_reg, offset
.cfi_escape DW_CFA_def_cfa_expression
cfi_emit_uleb128 (1 + DWARF_SLEB128_14BIT_SIZE(\offset) + 1)
.cfi_escape DW_OP_breg0 + (\target_reg)
cfi_emit_sleb128 (\offset)
.cfi_escape DW_OP_deref
.endm
// Set previous value of the register 'target_reg' to (context_reg + offset)
.macro cfi_set_prev_reg_value target_reg, context_reg, offset
.cfi_escape DW_CFA_expression
cfi_emit_uleb128 (\target_reg)
cfi_emit_uleb128 (1 + DWARF_SLEB128_14BIT_SIZE(\offset))
.cfi_escape DW_OP_breg0 + (\context_reg)
cfi_emit_sleb128 (\offset)
.endm
#endif // defined(__ASSEMBLER__)
@@ -0,0 +1,36 @@
#pragma once
#if !defined(__ASSEMBLER__)
#error "This file can only be used by assembly files."
#endif
#define PROC_START(name) \
.global name; \
.type name, @function; \
.cfi_startproc; \
name:
#define PROC_START_NOCFI(name) \
.global name; \
.type name, @function; \
name:
#define PROC_END(name) \
.cfi_endproc; \
.size name, . - name
#define PROC_END_NOCFI(name) \
.size name, . - name
#define PROC_ALIAS(name, alias) \
.global alias; \
.type alias, @function; \
.set alias, name
#define PROC_HIDDEN_ALIAS(name, alias) \
.hidden alias; \
.type alias, @function; \
.set alias, name
#define GNU_STACK_NOTE() \
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,32 @@
#ifndef MLIBC_ALL_SYSDEPS
#define MLIBC_ALL_SYSDEPS
#include <mlibc-config.h>
#include <internal-config.h>
/* The ANSI option is always enabled. */
#include <mlibc/ansi-sysdeps.hpp>
#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_RTLD
# include <mlibc/rtld-sysdeps.hpp>
#endif /* MLIBC_BUILDING_RTLD */
#include <mlibc/internal-sysdeps.hpp>
#endif /* MLIBC_ALL_SYSDEPS */
@@ -0,0 +1,38 @@
#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);
size_t get_size(void *ptr);
};
MemoryAllocator &getAllocator();
#endif // !MLIBC_DEBUG_ALLOCATOR
#endif // MLIBC_FRIGG_ALLOC
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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(path, 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 stats for " << path << 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 file_window to " << path << 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 file_window to " << path << frg::endlog;
}
// TODO: Write destructor to deallocate/unmap memory.
void *get() {
return _ptr;
}
private:
void *_ptr;
};
#endif // MLIBC_FILE_WINDOW
@@ -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
@@ -0,0 +1,19 @@
#ifndef MLIBC_GETOPT
#define MLIBC_GETOPT
struct option;
namespace mlibc {
enum GetoptMode {
Short,
Long,
LongOnly,
};
int getopt_common(int argc, char * const argv[], const char *optstring,
const struct option *longopts, int *longindex, enum GetoptMode mode);
} // namespace mlibc
#endif // MLIBC_GETOPT
@@ -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
@@ -0,0 +1,50 @@
#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>
#if defined(__riscv)
#include <abi-bits/riscv-hwprobe.h>
#include <bits/cpu_set.h>
#endif
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);
#if defined(__riscv)
[[gnu::weak]] int sys_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count, size_t cpusetsize, cpu_set_t *cpus, unsigned int flags);
#endif
} //namespace mlibc
#endif // MLIBC_INTERNAL_SYSDEPS
@@ -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
@@ -0,0 +1,129 @@
#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>
// alignas(4) is specified for the benefit of m68k, where default alignment is
// 2 bytes for a uint32_t, while the futex syscall requires 4-byte alignment.
// It is a no-op on any other architecture.
template<bool Recursive>
struct alignas(4) 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.
// Also loop around in case of a signal interrupting the wait
if (e && e != EAGAIN && e != EINTR)
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
@@ -0,0 +1,14 @@
#ifndef MLIBC_SEARCH
#define MLIBC_SEARCH
#include <bits/search.h>
namespace mlibc {
int hcreate_r(size_t num_entries, struct hsearch_data *htab);
void hdestroy_r(struct hsearch_data *htab);
int hsearch_r(ENTRY item, ACTION action, ENTRY **ret, struct hsearch_data *htab);
} // namespace mlibc
#endif // MLIBC_SEARCH
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,163 @@
#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'));
bool hasBinPrefix = hasOctalPrefix && (s[1] == widen<Char>('b') || s[1] == widen<Char>('B'));
// 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 == 2) && hasBinPrefix) {
s += 2;
base = 2;
} 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
@@ -0,0 +1,370 @@
#ifndef MLIBC_SYSDEP_SIGNATURES
#define MLIBC_SYSDEP_SIGNATURES
#include <abi-bits/mode_t.h>
#include <abi-bits/pid_t.h>
#include <abi-bits/resource.h>
#include <abi-bits/seek-whence.h>
#include <abi-bits/signal.h>
#include <abi-bits/stat.h>
#include <abi-bits/uid_t.h>
#include <abi-bits/vm-flags.h>
#include <bits/ansi/time_t.h>
#include <bits/ansi/timespec.h>
#include <bits/cpu_set.h>
#include <bits/off_t.h>
#include <bits/ssize_t.h>
#include <concepts>
#include <mlibc-config.h>
#include <mlibc/fsfd_target.hpp>
#include <stddef.h>
#include <stdint.h>
#include <mlibc/sysdep-tags.hpp>
namespace mlibc {
template<typename Tag>
struct SysdepImpl;
struct NoImpl {};
template<typename ST, typename Tag>
using SysdepOf = std::conditional_t<
std::derived_from<ST, Tag>,
mlibc::SysdepImpl<Tag>,
NoImpl
>;
#define SYSDEP_FUNC(name, ...) template <> struct SysdepImpl<name> { static int operator()(__VA_ARGS__); }
#define SYSDEP_FUNC_RET(rettype, name, ...) template <> struct SysdepImpl<name> { static rettype operator()(__VA_ARGS__); }
#define SYSDEP_FUNC_NORETURN(name, ...) template <> struct SysdepImpl<name> { [[noreturn]] static void operator()(__VA_ARGS__); }
SYSDEP_FUNC_RET(void, LibcLog, const char *message);
SYSDEP_FUNC_NORETURN(LibcPanic);
SYSDEP_FUNC_RET(pid_t, FutexTid);
SYSDEP_FUNC(AnonAllocate, size_t size, void **pointer);
SYSDEP_FUNC(AnonFree, void *pointer, size_t size);
SYSDEP_FUNC(Stat, mlibc::fsfd_target fsfdt, int fd, const char *path, int flags, struct stat *statbuf);
SYSDEP_FUNC(VmMap, void *hint, size_t size, int prot, int flags, int fd, off_t offset, void **window);
SYSDEP_FUNC(VmUnmap, void *pointer, size_t size);
SYSDEP_FUNC(VmProtect, void *pointer, size_t size, int prot);
SYSDEP_FUNC(TcbSet, void *pointer);
#if defined(__riscv)
SYSDEP_FUNC(RiscvHwprobe, struct riscv_hwprobe *pairs, size_t pair_count, size_t cpusetsize, cpu_set_t *cpus, unsigned int flags);
#endif
// ANSI option sysdeps
SYSDEP_FUNC_NORETURN(Exit, int status);
SYSDEP_FUNC_NORETURN(ThreadExit);
SYSDEP_FUNC(PrepareStack, void **stack, void *entry, void *user_arg, void* tcb, size_t *stack_size, size_t *guard_size, void **stack_base);
SYSDEP_FUNC(Clone, void *tcb, pid_t *pid_out, void *stack);
SYSDEP_FUNC(FutexWait, int *pointer, int expected, const struct timespec *time);
SYSDEP_FUNC(FutexWake, int *pointer, bool all);
SYSDEP_FUNC(Open, const char *pathname, int flags, mode_t mode, int *fd);
SYSDEP_FUNC(Flock, int fd, int options);
SYSDEP_FUNC(OpenDir, const char *path, int *handle);
SYSDEP_FUNC(ReadEntries, int handle, void *buffer, size_t max_size, size_t *bytes_read);
SYSDEP_FUNC(Read, int fd, void *buf, size_t count, ssize_t *bytes_read);
SYSDEP_FUNC(Write, int fd, const void *buf, size_t count, ssize_t *bytes_written);
SYSDEP_FUNC(Pread, int fd, void *buf, size_t n, off_t off, ssize_t *bytes_read);
SYSDEP_FUNC(Seek, int fd, off_t offset, int whence, off_t *new_offset);
SYSDEP_FUNC(Close, int fd);
SYSDEP_FUNC(ClockGet, int clock, time_t *secs, long *nanos);
SYSDEP_FUNC(ClockSet, int clock, time_t secs, long nanos);
SYSDEP_FUNC(ClockGetres, int clock, time_t *secs, long *nanos);
SYSDEP_FUNC(Sleep, time_t *secs, long *nanos);
SYSDEP_FUNC(Isatty, int fd);
SYSDEP_FUNC(Rmdir, const char *path);
SYSDEP_FUNC(Unlinkat, int dirfd, const char *path, int flags);
SYSDEP_FUNC(Rename, const char *path, const char *new_path);
SYSDEP_FUNC(FdToPath, int fd, char **out);
SYSDEP_FUNC(Sigprocmask, int how, const sigset_t *__restrict set, sigset_t *__restrict retrieve);
SYSDEP_FUNC(Sigaction, int, const struct sigaction *__restrict, struct sigaction *__restrict);
SYSDEP_FUNC(Fork, pid_t *child);
SYSDEP_FUNC(Waitpid, pid_t pid, int *status, int flags, struct rusage *ru, pid_t *ret_pid);
SYSDEP_FUNC(Execve, const char *path, char *const argv[], char *const envp[]);
SYSDEP_FUNC_RET(void, Yield);
SYSDEP_FUNC_RET(pid_t, GetPid);
SYSDEP_FUNC(Kill, pid_t, int);
#if MLIBC_BUILDING_RTLD
SYSDEP_FUNC(VmReadahead, void *pointer, size_t size);
#endif /* MLIBC_BUILDING_RTLD */
#if __MLIBC_POSIX_OPTION
SYSDEP_FUNC(Readv, int fd, const struct iovec *iovs, int iovc, ssize_t *bytes_read);
SYSDEP_FUNC(Writev, int fd, const struct iovec *iovs, int iovc, ssize_t *bytes_written);
SYSDEP_FUNC(Pwrite, int fd, const void *buf, size_t n, off_t off, ssize_t *bytes_read);
SYSDEP_FUNC(Access, const char *path, int mode);
SYSDEP_FUNC(Faccessat, int dirfd, const char *pathname, int mode, int flags);
SYSDEP_FUNC(Dup, int fd, int flags, int *newfd);
SYSDEP_FUNC(Dup2, int fd, int flags, int newfd);
SYSDEP_FUNC(Statvfs, const char *path, struct statvfs *out);
SYSDEP_FUNC(Fstatvfs, int fd, struct statvfs *out);
SYSDEP_FUNC(Readlink, const char *path, void *buffer, size_t max_size, ssize_t *length);
SYSDEP_FUNC(Readlinkat, int dirfd, const char *path, void *buffer, size_t max_size, ssize_t *length);
SYSDEP_FUNC(Truncate, const char *path, off_t length);
SYSDEP_FUNC(Ftruncate, int fd, size_t size);
SYSDEP_FUNC(Fallocate, int fd, off_t offset, size_t size);
SYSDEP_FUNC(Openat, int dirfd, const char *path, int flags, mode_t mode, int *fd);
SYSDEP_FUNC(Socket, int family, int type, int protocol, int *fd);
SYSDEP_FUNC(MsgSend, int fd, const struct msghdr *hdr, int flags, ssize_t *length);
SYSDEP_FUNC(Sendto, int fd, const void *buffer, size_t size, int flags, const struct sockaddr *sock_addr, socklen_t addr_length, ssize_t *length);
SYSDEP_FUNC(MsgRecv, int fd, struct msghdr *hdr, int flags, ssize_t *length);
SYSDEP_FUNC(Recvfrom, int fd, void *buffer, size_t size, int flags, struct sockaddr *sock_addr, socklen_t *addr_length, ssize_t *length);
SYSDEP_FUNC(Listen, int fd, int backlog);
SYSDEP_FUNC_RET(gid_t, GetGid);
SYSDEP_FUNC_RET(gid_t, GetEgid);
SYSDEP_FUNC_RET(uid_t, GetUid);
SYSDEP_FUNC_RET(uid_t, GetEuid);
SYSDEP_FUNC_RET(pid_t, GetTid);
SYSDEP_FUNC_RET(pid_t, GetPpid);
SYSDEP_FUNC(GetPgid, pid_t pid, pid_t *pgid);
SYSDEP_FUNC(GetSid, pid_t pid, pid_t *sid);
SYSDEP_FUNC(SetPgid, pid_t pid, pid_t pgid);
SYSDEP_FUNC(SetUid, uid_t uid);
SYSDEP_FUNC(SetEuid, uid_t euid);
SYSDEP_FUNC(SetGid, gid_t gid);
SYSDEP_FUNC(SetEgid, gid_t egid);
SYSDEP_FUNC(GetGroups, size_t size, gid_t *list, int *ret);
SYSDEP_FUNC(Fexecve, int fd, char *const argv[], char *const envp[]);
SYSDEP_FUNC(Pselect, int num_fds, fd_set *read_set, fd_set *write_set, fd_set *except_set, const struct timespec *timeout, const sigset_t *sigmask, int *num_events);
SYSDEP_FUNC(GetRusage, int scope, struct rusage *usage);
SYSDEP_FUNC(GetRlimit, int resource, struct rlimit *limit);
SYSDEP_FUNC(SetRlimit, int resource, const struct rlimit *limit);
SYSDEP_FUNC(GetPriority, int which, id_t who, int *value);
SYSDEP_FUNC(SetPriority, int which, id_t who, int prio);
SYSDEP_FUNC(GetSchedparam, void *tcb, int *policy, struct sched_param *param);
SYSDEP_FUNC(SetSchedparam, void *tcb, int policy, const struct sched_param *param);
SYSDEP_FUNC(GetScheduler, pid_t pid, int *policy);
SYSDEP_FUNC(SetScheduler, pid_t pid, int policy, const struct sched_param *param);
SYSDEP_FUNC(GetParam, pid_t pid, struct sched_param *param);
SYSDEP_FUNC(SetParam, pid_t pid, const struct sched_param *param);
SYSDEP_FUNC(GetMaxPriority, int policy, int *out);
SYSDEP_FUNC(GetMinPriority, int policy, int *out);
SYSDEP_FUNC(GetCwd, char *buffer, size_t size);
SYSDEP_FUNC(Chdir, const char *path);
SYSDEP_FUNC(Fchdir, int fd);
SYSDEP_FUNC(Chroot, const char *path);
SYSDEP_FUNC(Mkdir, const char *path, mode_t mode);
SYSDEP_FUNC(Mkdirat, int dirfd, const char *path, mode_t mode);
SYSDEP_FUNC(Link, const char *old_path, const char *new_path);
SYSDEP_FUNC(Linkat, int olddirfd, const char *old_path, int newdirfd, const char *new_path, int flags);
SYSDEP_FUNC(Symlink, const char *target_path, const char *link_path);
SYSDEP_FUNC(Symlinkat, const char *target_path, int dirfd, const char *link_path);
SYSDEP_FUNC(Renameat, int olddirfd, const char *old_path, int newdirfd, const char *new_path);
SYSDEP_FUNC(Fcntl, int fd, int request, va_list args, int *result);
SYSDEP_FUNC(Ttyname, int fd, char *buf, size_t size);
SYSDEP_FUNC(Fadvise, int fd, off_t offset, off_t length, int advice);
SYSDEP_FUNC_RET(void, Sync);
SYSDEP_FUNC(Fsync, int fd);
SYSDEP_FUNC(Fdatasync, int fd);
SYSDEP_FUNC(Chmod, const char *pathname, mode_t mode);
SYSDEP_FUNC(Fchmod, int fd, mode_t mode);
SYSDEP_FUNC(Fchmodat, int fd, const char *pathname, mode_t mode, int flags);
SYSDEP_FUNC(Utimensat, int dirfd, const char *pathname, const struct timespec times[2], int flags);
SYSDEP_FUNC(Mlock, const void *addr, size_t length);
SYSDEP_FUNC(Munlock, const void *addr, size_t length);
SYSDEP_FUNC(Mlockall, int flags);
SYSDEP_FUNC(Munlockall);
SYSDEP_FUNC(Mincore, void *addr, size_t length, unsigned char *vec);
SYSDEP_FUNC(VmRemap, void *pointer, size_t size, size_t new_size, void **window);
SYSDEP_FUNC(SetSid, pid_t *sid);
SYSDEP_FUNC(Tcgetattr, int fd, struct termios *attr);
SYSDEP_FUNC(Tcsetattr, int, int, const struct termios *attr);
SYSDEP_FUNC(Tcsendbreak, int fd, int dur);
SYSDEP_FUNC(Tcflow, int, int);
SYSDEP_FUNC(Tcflush, int fd, int queue);
SYSDEP_FUNC(Tcdrain, int);
SYSDEP_FUNC(Tcgetwinsize, int fd, struct winsize *winsz);
SYSDEP_FUNC(Tcsetwinsize, int fd, const struct winsize *winsz);
SYSDEP_FUNC(Pipe, int *fds, int flags);
SYSDEP_FUNC(Socketpair, int domain, int type_and_flags, int proto, int *fds);
SYSDEP_FUNC(Poll, struct pollfd *fds, nfds_t count, int timeout, int *num_events);
SYSDEP_FUNC(Ppoll, struct pollfd *fds, nfds_t count, const struct timespec *ts, const sigset_t *mask, int *num_events);
SYSDEP_FUNC(Ioctl, int fd, unsigned long request, void *arg, int *result);
SYSDEP_FUNC(PosixDevctl, int fd, int dcmd, void *__restrict dev_data_ptr, size_t nbyte, int *__restrict dev_info_ptr);
SYSDEP_FUNC(GetSockopt, int fd, int layer, int number, void *__restrict buffer, socklen_t *__restrict size);
SYSDEP_FUNC(SetSockopt, int fd, int layer, int number, const void *buffer, socklen_t size);
SYSDEP_FUNC(Shutdown, int sockfd, int how);
SYSDEP_FUNC(Sockatmark, int sockfd, int *out);
SYSDEP_FUNC(ThreadSigmask, int how, const sigset_t *__restrict set, sigset_t *__restrict retrieve);
SYSDEP_FUNC(Sigtimedwait, const sigset_t *__restrict set, siginfo_t *__restrict info, const struct timespec *__restrict timeout, int *out_signal);
SYSDEP_FUNC(Accept, int fd, int *newfd, struct sockaddr *addr_ptr, socklen_t *addr_length, int flags);
SYSDEP_FUNC(Bind, int fd, const struct sockaddr *addr_ptr, socklen_t addr_length);
SYSDEP_FUNC(Connect, int fd, const struct sockaddr *addr_ptr, socklen_t addr_length);
SYSDEP_FUNC(Sockname, int fd, struct sockaddr *addr_ptr, socklen_t max_addr_length, socklen_t *actual_length);
SYSDEP_FUNC(Peername, int fd, struct sockaddr *addr_ptr, socklen_t max_addr_length, socklen_t *actual_length);
SYSDEP_FUNC(GetHostname, char *buffer, size_t bufsize);
SYSDEP_FUNC(SetHostname, const char *buffer, size_t bufsize);
SYSDEP_FUNC(Mkfifoat, int dirfd, const char *path, mode_t mode);
SYSDEP_FUNC(GetEntropy, void *buffer, size_t length);
SYSDEP_FUNC(Mknodat, int dirfd, const char *path, int mode, int dev);
SYSDEP_FUNC(Umask, mode_t mode, mode_t *old);
SYSDEP_FUNC(BeforeCancellableSyscall, ucontext_t *uctx);
SYSDEP_FUNC(Tgkill, int tgid, int tid, int sig);
SYSDEP_FUNC(Fchownat, int dirfd, const char *pathname, uid_t owner, gid_t group, int flags);
SYSDEP_FUNC(Sigaltstack, const stack_t *ss, stack_t *oss);
SYSDEP_FUNC(Sigsuspend, const sigset_t *set);
SYSDEP_FUNC(Sigpending, sigset_t *set);
SYSDEP_FUNC(Sigqueue, pid_t pid, int sig, const union sigval val);
SYSDEP_FUNC(SetGroups, size_t size, const gid_t *list);
SYSDEP_FUNC(MemfdCreate, const char *name, int flags, int *fd);
SYSDEP_FUNC(Madvise, void *addr, size_t length, int advice);
SYSDEP_FUNC(PosixMadvise, void *addr, size_t length, int advice);
SYSDEP_FUNC(Msync, void *addr, size_t length, int flags);
SYSDEP_FUNC(GetItimer, int which, struct itimerval *curr_value);
SYSDEP_FUNC(SetItimer, int which, const struct itimerval *new_value, struct itimerval *old_value);
SYSDEP_FUNC(TimerCreate, clockid_t clk, struct sigevent *__restrict evp, timer_t *__restrict res);
SYSDEP_FUNC(TimerSettime, timer_t t, int flags, const struct itimerspec *__restrict val, struct itimerspec *__restrict old);
SYSDEP_FUNC(TimerGettime, timer_t t, struct itimerspec *val);
SYSDEP_FUNC(TimerDelete, timer_t t);
SYSDEP_FUNC(TimerGetoverrun, timer_t t, int *out);
SYSDEP_FUNC(Times, struct tms *tms, clock_t *out);
SYSDEP_FUNC(Uname, struct utsname *buf);
SYSDEP_FUNC(Pause);
SYSDEP_FUNC(SetResuid, uid_t ruid, uid_t euid, uid_t suid);
SYSDEP_FUNC(SetResgid, gid_t rgid, gid_t egid, gid_t sgid);
SYSDEP_FUNC(GetResuid, uid_t *ruid, uid_t *euid, uid_t *suid);
SYSDEP_FUNC(GetResgid, gid_t *rgid, gid_t *egid, gid_t *sgid);
SYSDEP_FUNC(SetReuid, uid_t ruid, uid_t euid);
SYSDEP_FUNC(SetRegid, gid_t rgid, gid_t egid);
SYSDEP_FUNC(GetLoginR, char *name, size_t name_len);
SYSDEP_FUNC(IfIndextoname, unsigned int index, char *name);
SYSDEP_FUNC(IfNametoindex, const char *name, unsigned int *ret);
SYSDEP_FUNC(Ptsname, int fd, char *buffer, size_t length);
SYSDEP_FUNC(Unlockpt, int fd);
SYSDEP_FUNC(ThreadSetname, void *tcb, const char *name);
SYSDEP_FUNC(ThreadGetname, void *tcb, char *name, size_t size);
SYSDEP_FUNC(Sysconf, int num, long *ret);
SYSDEP_FUNC(Semget, key_t key, int n, int fl, int *id);
SYSDEP_FUNC(Semctl, int semid, int semnum, int cmd, void *semun, int *ret);
SYSDEP_FUNC(GetAffinity, pid_t pid, size_t cpusetsize, cpu_set_t *mask);
SYSDEP_FUNC(GetThreadaffinity, pid_t tid, size_t cpusetsize, cpu_set_t *mask);
SYSDEP_FUNC(SetAffinity, pid_t pid, size_t cpusetsize, const cpu_set_t *mask);
SYSDEP_FUNC(SetThreadaffinity, pid_t tid, size_t cpusetsize, const cpu_set_t *mask);
SYSDEP_FUNC(GetCurrentStackInfo, void **stack_base, size_t *stack_size);
SYSDEP_FUNC(Waitid, idtype_t idtype, id_t id, siginfo_t *info, int options);
SYSDEP_FUNC(NameToHandleAt, int dirfd, const char *pathname, struct file_handle *handle, int *mount_id, int flags);
SYSDEP_FUNC(Splice, int in_fd, off_t *in_off, int out_fd, off_t *out_off, size_t size, unsigned int flags, ssize_t *out);
SYSDEP_FUNC(Shmat, void **seg_start, int shmid, const void *shmaddr, int shmflg);
SYSDEP_FUNC(Shmctl, int *idx, int shmid, int cmd, struct shmid_ds *buf);
SYSDEP_FUNC(Shmdt, const void *shmaddr);
SYSDEP_FUNC(Shmget, int *shm_id, key_t key, size_t size, int shmflg);
SYSDEP_FUNC(InetConfigured, bool *ipv4, bool *ipv6);
SYSDEP_FUNC(Nice, int nice, int *new_nice);
SYSDEP_FUNC(Openpt, int oflags, int *fd);
SYSDEP_FUNC(Msgctl, int q, int cmd, struct msqid_ds *buf);
SYSDEP_FUNC(Msgget, key_t k, int flag, int *out);
SYSDEP_FUNC(Msgrcv, int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg, ssize_t *out);
SYSDEP_FUNC(Msgsnd, int msqid, const void *msgp, size_t msgsz, int msgflg);
SYSDEP_FUNC(MqOpen, const char *name, int oflag, mode_t mode, mq_attr *attr, mqd_t *out);
SYSDEP_FUNC(MqUnlink, const char *name);
SYSDEP_FUNC(MqReceive, mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);
SYSDEP_FUNC(MqGetAttr, mqd_t mqdes, mq_attr *mqstat);
SYSDEP_FUNC(MqSetAttr, mqd_t mqdes, const mq_attr *mqstat, mq_attr *omqstat);
#endif // __MLIBC_POSIX_OPTION
#if __MLIBC_LINUX_OPTION
SYSDEP_FUNC(InotifyCreate, int flags, int *fd);
SYSDEP_FUNC(InotifyAddWatch, int ifd, const char *path, uint32_t mask, int *wd);
SYSDEP_FUNC(InotifyRmWatch, int ifd, int wd);
SYSDEP_FUNC(Mount, const char *source, const char *target, const char *fstype, unsigned long flags, const void *data);
SYSDEP_FUNC(Umount2, const char *target, int flags);
SYSDEP_FUNC(Ptrace, long req, pid_t pid, void *addr, void *data, long *out);
SYSDEP_FUNC(Capget, cap_user_header_t hdrp, cap_user_data_t datap);
SYSDEP_FUNC(Capset, cap_user_header_t hdrp, const cap_user_data_t datap);
SYSDEP_FUNC(Prctl, int option, va_list va, int *out);
SYSDEP_FUNC(InitModule, void *module, unsigned long length, const char *args);
SYSDEP_FUNC(DeleteModule, const char *name, unsigned flags);
SYSDEP_FUNC(Klogctl, int type, char *bufp, int len, int *out);
SYSDEP_FUNC(Getcpu, int *cpu);
SYSDEP_FUNC(Sysinfo, struct sysinfo *info);
SYSDEP_FUNC(Swapon, const char *path, int flags);
SYSDEP_FUNC(Swapoff, const char *path);
SYSDEP_FUNC(Setxattr, const char *path, const char *name, const void *val, size_t size, int flags);
SYSDEP_FUNC(Lsetxattr, const char *path, const char *name, const void *val, size_t size, int flags);
SYSDEP_FUNC(Fsetxattr, int fd, const char *name, const void *val, size_t size, int flags);
SYSDEP_FUNC(Getxattr, const char *path, const char *name, void *val, size_t size, ssize_t *nread);
SYSDEP_FUNC(Lgetxattr, const char *path, const char *name, void *val, size_t size, ssize_t *nread);
SYSDEP_FUNC(Fgetxattr, int fd, const char *name, void *val, size_t size, ssize_t *nread);
SYSDEP_FUNC(Listxattr, const char *path, char *list, size_t size, ssize_t *nread);
SYSDEP_FUNC(Llistxattr, const char *path, char *list, size_t size, ssize_t *nread);
SYSDEP_FUNC(Flistxattr, int fd, char *list, size_t size, ssize_t *nread);
SYSDEP_FUNC(Removexattr, const char *path, const char *name);
SYSDEP_FUNC(Lremovexattr, const char *path, const char *name);
SYSDEP_FUNC(Fremovexattr, int fd, const char *name);
SYSDEP_FUNC(Statfs, const char *path, struct statfs *buf);
SYSDEP_FUNC(Fstatfs, int fd, struct statfs *buf);
SYSDEP_FUNC(Statx, int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf);
SYSDEP_FUNC(Getifaddrs, struct ifaddrs **);
SYSDEP_FUNC(Sendfile, int outfd, int infd, off_t *offset, size_t count, ssize_t *out);
SYSDEP_FUNC(Syncfs, int fd);
SYSDEP_FUNC(Unshare, int flags);
SYSDEP_FUNC(SetNs, int fd, int nstype);
SYSDEP_FUNC(PidfdOpen, pid_t pid, unsigned int flags, int *outfd);
SYSDEP_FUNC(PidfdGetpid, int fd, pid_t *outpid);
SYSDEP_FUNC(PidfdSendSignal, int pidfd, int sig, siginfo_t *info, unsigned int flags);
SYSDEP_FUNC(ProcessVmReadv, pid_t pid, const struct iovec *local_iov, unsigned long liovcnt, const struct iovec *remote_iov, unsigned long riovcnt, unsigned long flags, ssize_t *out);
SYSDEP_FUNC(ProcessVmWritev, pid_t pid, const struct iovec *local_iov, unsigned long liovcnt, const struct iovec *remote_iov, unsigned long riovcnt, unsigned long flags, ssize_t *out);
SYSDEP_FUNC(Fsopen, const char *fsname, unsigned int flags, int *outfd);
SYSDEP_FUNC(Fsmount, int fsfd, unsigned int flags, unsigned int mountflags, int *outfd);
SYSDEP_FUNC(Fsconfig, int fd, unsigned int cmd, const char *key, const void *val, int aux);
SYSDEP_FUNC(MoveMount, int from_dirfd, const char *from_path, int to_dirfd, const char *to_path, unsigned int flags);
SYSDEP_FUNC(OpenTree, int dirfd, const char *path, unsigned int flags, int *outfd);
SYSDEP_FUNC(CopyFileRange, int fd_in, off_t *off_in, int fd_out, off_t *off_out, size_t count, unsigned int flags, ssize_t *bytes_copied);
#endif // __MLIBC_LINUX_OPTION
#if __MLIBC_LINUX_EPOLL_OPTION
SYSDEP_FUNC(EpollCreate, int flags, int *fd);
SYSDEP_FUNC(EpollCtl, int epfd, int mode, int fd, struct epoll_event *ev);
SYSDEP_FUNC(EpollPwait, int epfd, struct epoll_event *ev, int n, int timeout, const sigset_t *sigmask, int *raised);
#endif // __MLIBC_LINUX_EPOLL_OPTION
#if __MLIBC_LINUX_TIMERFD_OPTION
SYSDEP_FUNC(TimerfdCreate, int clockid, int flags, int *fd);
SYSDEP_FUNC(TimerfdSettime, int fd, int flags, const struct itimerspec *value, struct itimerspec *oldvalue);
SYSDEP_FUNC(TimerfdGettime, int fd, struct itimerspec *its);
#endif // __MLIBC_LINUX_TIMERFD_OPTION
#if __MLIBC_LINUX_SIGNALFD_OPTION
SYSDEP_FUNC(SignalfdCreate, const sigset_t *, int flags, int *fd);
#endif // __MLIBC_LINUX_SIGNALFD_OPTION
#if __MLIBC_LINUX_EVENTFD_OPTION
SYSDEP_FUNC(EventfdCreate, unsigned int initval, int flags, int *fd);
#endif // __MLIBC_LINUX_EVENTFD_OPTION
#if __MLIBC_LINUX_REBOOT_OPTION
SYSDEP_FUNC(Reboot, int cmd);
#endif // __MLIBC_LINUX_REBOOT_OPTION
#if __MLIBC_LINUX_WRAPPERS_OPTION
SYSDEP_FUNC(RtSigqueueinfo, pid_t tgid, int sig, siginfo_t *uinfo);
SYSDEP_FUNC(RtTgSigqueueinfo, pid_t tgid, pid_t tid, int sig, siginfo_t *uinfo);
#endif // __MLIBC_LINUX_WRAPPERS_OPTION
#if __MLIBC_BSD_OPTION
SYSDEP_FUNC(Brk, void **out);
SYSDEP_FUNC(GetLoadavg, double *samples);
SYSDEP_FUNC(Openpty, int *mfd, int *sfd, char *name, const struct termios *ios, const struct winsize *win);
#endif // __MLIBC_BSD_OPTION
#if __MLIBC_GLIBC_OPTION
SYSDEP_FUNC(Personality, unsigned long persona, int *out);
SYSDEP_FUNC(Ioperm, unsigned long int from, unsigned long int num, int turn_on);
SYSDEP_FUNC(Iopl, int level);
#ifdef __riscv
SYSDEP_FUNC(RiscvFlushIcache, void *start, void *end, unsigned long flags);
#endif // __riscv
#endif // __MLIBC_GLIBC_OPTION
#undef SYSDEP_FUNC
#undef SYSDEP_FUNC_RET
#undef SYSDEP_FUNC_NORETURN
} // namespace mlibc
#endif /* MLIBC_SYSDEP_SIGNATURES */
@@ -0,0 +1,703 @@
#ifndef MLIBC_SYSDEP_TAGS
#define MLIBC_SYSDEP_TAGS
#include <abi-bits/mode_t.h>
#include <abi-bits/pid_t.h>
#include <abi-bits/resource.h>
#include <abi-bits/seek-whence.h>
#include <abi-bits/signal.h>
#include <abi-bits/stat.h>
#include <abi-bits/uid_t.h>
#include <abi-bits/vm-flags.h>
#include <bits/ansi/time_t.h>
#include <bits/ansi/timespec.h>
#include <bits/cpu_set.h>
#include <bits/off_t.h>
#include <bits/ssize_t.h>
#include <concepts>
#include <mlibc-config.h>
#include <mlibc/fsfd_target.hpp>
#include <stddef.h>
#include <stdint.h>
// [[noreturn]] void sys_exit(int status);
struct Exit { static constexpr bool is_noreturn = true; };
// [[noreturn]] void sys_thread_exit();
struct ThreadExit { static constexpr bool is_noreturn = true; };
// If *stack is not null, it should point to the lowest addressable byte of the stack.
// Returns the new stack pointer in *stack and the stack base in *stack_base.
// int sys_prepare_stack(void **stack, void *entry, void *user_arg, void* tcb, size_t *stack_size, size_t *guard_size, void **stack_base);
struct PrepareStack {};
// int sys_clone(void *tcb, pid_t *pid_out, void *stack);
struct Clone {};
// int sys_futex_wait(int *pointer, int expected, const struct timespec *time);
struct FutexWait {};
// int sys_futex_wake(int *pointer, bool all);
struct FutexWake {};
// int sys_open(const char *pathname, int flags, mode_t mode, int *fd);
struct Open {};
// int sys_flock(int fd, int options);
struct Flock {};
// int sys_open_dir(const char *path, int *handle);
struct OpenDir {};
// int sys_read_entries(int handle, void *buffer, size_t max_size, size_t *bytes_read);
struct ReadEntries {};
// int sys_read(int fd, void *buf, size_t count, ssize_t *bytes_read);
struct Read {};
// int sys_write(int fd, const void *buf, size_t count, ssize_t *bytes_written);
struct Write {};
// int sys_pread(int fd, void *buf, size_t n, off_t off, ssize_t *bytes_read);
struct Pread {};
// int sys_seek(int fd, off_t offset, int whence, off_t *new_offset);
struct Seek {};
// int sysdep<Close>(int fd);
struct Close {};
// int sys_clock_get(int clock, time_t *secs, long *nanos);
struct ClockGet {};
// int sys_clock_set(int clock, time_t secs, long nanos);
struct ClockSet {};
// int sys_clock_getres(int clock, time_t *secs, long *nanos);
struct ClockGetres {};
// int sys_sleep(time_t *secs, long *nanos);
struct Sleep {};
// // In contrast to the isatty() library function, the sysdep function uses return value
// // zero (and not one) to indicate that the file is a terminal.
// int sys_isatty(int fd);
struct Isatty {};
// int sys_rmdir(const char *path);
struct Rmdir {};
// int sys_unlinkat(int dirfd, const char *path, int flags);
struct Unlinkat {};
// int sys_rename(const char *path, const char *new_path);
struct Rename {};
// int sys_fd_to_path(int fd, char **path);
struct FdToPath {};
// int sys_sigprocmask(int how, const sigset_t *__restrict set, sigset_t *__restrict retrieve);
struct Sigprocmask {};
// int sys_sigaction(int, const struct sigaction *__restrict, struct sigaction *__restrict);
struct Sigaction {};
// int sys_fork(pid_t *child);
struct Fork {};
// int sys_waitpid(pid_t pid, int *status, int flags, struct rusage *ru, pid_t *ret_pid);
struct Waitpid {};
// int sys_execve(const char *path, char *const argv[], char *const envp[]);
struct Execve {};
// void sys_yield();
struct Yield {};
// pid_t sys_getpid();
struct GetPid {};
// int sys_kill(pid_t, int);
struct Kill {};
// int sys_tcb_set(void *pointer);
struct TcbSet {};
#if MLIBC_BUILDING_RTLD
// int sys_vm_readahead(void *pointer, size_t size);
struct VmReadahead {};
#endif /* MLIBC_BUILDING_RTLD */
// void sys_libc_log(const char *message);
struct LibcLog {};
// [[noreturn]] void sys_libc_panic();
struct LibcPanic { static constexpr bool is_noreturn = true; };
// int sys_futex_tid();
struct FutexTid {};
// int sys_anon_allocate(size_t size, void **pointer);
struct AnonAllocate {};
// int sys_anon_free(void *pointer, size_t size);
struct AnonFree {};
// int sys_stat(fsfd_target fsfdt, int fd, const char *path, int flags, struct stat *statbuf);
struct Stat {};
// int sys_vm_map(void *hint, size_t size, int prot, int flags, int fd, off_t offset, void **window);
struct VmMap {};
// int sys_vm_unmap(void *pointer, size_t size);
struct VmUnmap {};
// int sys_vm_protect(void *pointer, size_t size, int prot);
struct VmProtect {};
#if defined(__riscv)
#include <sys/hwprobe.h>
// int sys_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count, size_t cpusetsize, cpu_set_t *cpus, unsigned int flags);
struct RiscvHwprobe {};
#endif
#if __MLIBC_BSD_OPTION
#include <termios.h>
// int sys_brk(void **out);
struct Brk {};
// int sys_getloadavg(double *samples);
struct GetLoadavg {};
// int sys_openpty(int *mfd, int *sfd, char *name, const struct termios *ios, const struct winsize *win);
struct Openpty {};
#endif // __MLIBC_BSD_OPTION
#if __MLIBC_GLIBC_OPTION
// int sys_personality(unsigned long persona, int *out);
struct Personality {};
// int sys_ioperm(unsigned long int from, unsigned long int num, int turn_on);
struct Ioperm {};
// int sys_iopl(int level);
struct Iopl {};
#ifdef __riscv
// int sys_riscv_flush_icache(void *start, void *end, unsigned long flags);
struct RiscvFlushIcache {};
#endif
#endif // __MLIBC_GLIBC_OPTION
#if __MLIBC_POSIX_OPTION
#include <abi-bits/msg.h>
#include <abi-bits/seek-whence.h>
#include <abi-bits/socklen_t.h>
#include <abi-bits/vm-flags.h>
#include <bits/off_t.h>
#include <bits/posix/stat.h>
#include <bits/ssize_t.h>
#include <fcntl.h>
#include <mlibc/fsfd_target.hpp>
#include <mqueue.h>
#include <poll.h>
#include <sched.h>
#include <stdarg.h>
#include <sys/poll.h>
#include <sys/resource.h>
#include <sys/select.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <sys/statvfs.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <termios.h>
#include <time.h>
#include <ucontext.h>
// int sys_readv(int fd, const struct iovec *iovs, int iovc, ssize_t *bytes_read);
struct Readv {};
// int sys_writev(int fd, const struct iovec *iovs, int iovc, ssize_t *bytes_written);
struct Writev {};
// int sys_pwrite(int fd, const void *buf, size_t n, off_t off, ssize_t *bytes_read);
struct Pwrite {};
// int sys_access(const char *path, int mode);
struct Access {};
// int sys_faccessat(int dirfd, const char *pathname, int mode, int flags);
struct Faccessat {};
// int sys_dup(int fd, int flags, int *newfd);
struct Dup {};
// int sys_dup2(int fd, int flags, int newfd);
struct Dup2 {};
// int sys_statvfs(const char *path, struct statvfs *out);
struct Statvfs {};
// int sys_fstatvfs(int fd, struct statvfs *out);
struct Fstatvfs {};
// int sys_readlink(const char *path, void *buffer, size_t max_size, ssize_t *length);
struct Readlink {};
// int sys_readlinkat(int dirfd, const char *path, void *buffer, size_t max_size, ssize_t *length);
struct Readlinkat {};
// int sys_truncate(const char *path, off_t length);
struct Truncate {};
// int sys_ftruncate(int fd, size_t size);
struct Ftruncate {};
// int sys_fallocate(int fd, off_t offset, size_t size);
struct Fallocate {};
// int sys_openat(int dirfd, const char *path, int flags, mode_t mode, int *fd);
struct Openat {};
// int sys_socket(int family, int type, int protocol, int *fd);
struct Socket {};
// int sys_msg_send(int fd, const struct msghdr *hdr, int flags, ssize_t *length);
struct MsgSend {};
// int sys_sendto(int fd, const void *buffer, size_t size, int flags, const struct sockaddr *sock_addr, socklen_t addr_length, ssize_t *length);
struct Sendto {};
// int sys_msg_recv(int fd, struct msghdr *hdr, int flags, ssize_t *length);
struct MsgRecv {};
// int sys_recvfrom(int fd, void *buffer, size_t size, int flags, struct sockaddr *sock_addr, socklen_t *addr_length, ssize_t *length);
struct Recvfrom {};
// int sys_listen(int fd, int backlog);
struct Listen {};
// gid_t sys_getgid();
struct GetGid {};
// gid_t sys_getegid();
struct GetEgid {};
// uid_t sys_getuid();
struct GetUid {};
// uid_t sys_geteuid();
struct GetEuid {};
// pid_t sys_gettid();
struct GetTid {};
// pid_t sys_getppid();
struct GetPpid {};
// int sys_getpgid(pid_t pid, pid_t *pgid);
struct GetPgid {};
// int sys_getsid(pid_t pid, pid_t *sid);
struct GetSid {};
// int sys_setpgid(pid_t pid, pid_t pgid);
struct SetPgid {};
// int sys_setuid(uid_t uid);
struct SetUid {};
// int sys_seteuid(uid_t euid);
struct SetEuid {};
// int sys_setgid(gid_t gid);
struct SetGid {};
// int sys_setegid(gid_t egid);
struct SetEgid {};
// int sys_getgroups(size_t size, gid_t *list, int *ret);
struct GetGroups {};
// int sys_fexecve(int fd, char *const argv[], char *const envp[]);
struct Fexecve {};
// int sys_pselect(int num_fds, fd_set *read_set, fd_set *write_set, fd_set *except_set, const struct timespec *timeout, const sigset_t *sigmask, int *num_events);
struct Pselect {};
// int sys_getrusage(int scope, struct rusage *usage);
struct GetRusage {};
// int sys_getrlimit(int resource, struct rlimit *limit);
struct GetRlimit {};
// int sys_setrlimit(int resource, const struct rlimit *limit);
struct SetRlimit {};
// int sys_getpriority(int which, id_t who, int *value);
struct GetPriority {};
// int sys_setpriority(int which, id_t who, int prio);
struct SetPriority {};
// int sys_getschedparam(void *tcb, int *policy, struct sched_param *param);
struct GetSchedparam {};
// int sys_setschedparam(void *tcb, int policy, const struct sched_param *param);
struct SetSchedparam {};
// int sys_getscheduler(pid_t pid, int *policy);
struct GetScheduler {};
// int sys_setscheduler(pid_t pid, int policy, const struct sched_param *param);
struct SetScheduler {};
// int sys_getparam(pid_t pid, struct sched_param *param);
struct GetParam {};
// int sys_setparam(pid_t pid, const struct sched_param *param);
struct SetParam {};
// int sys_get_max_priority(int policy, int *out);
struct GetMaxPriority {};
// int sys_get_min_priority(int policy, int *out);
struct GetMinPriority {};
// int sys_getcwd(char *buffer, size_t size);
struct GetCwd {};
// int sys_chdir(const char *path);
struct Chdir {};
// int sys_fchdir(int fd);
struct Fchdir {};
// int sys_chroot(const char *path);
struct Chroot {};
// int sys_mkdir(const char *path, mode_t mode);
struct Mkdir {};
// int sys_mkdirat(int dirfd, const char *path, mode_t mode);
struct Mkdirat {};
// int sys_link(const char *old_path, const char *new_path);
struct Link {};
// int sys_linkat(int olddirfd, const char *old_path, int newdirfd, const char *new_path, int flags);
struct Linkat {};
// int sys_symlink(const char *target_path, const char *link_path);
struct Symlink {};
// int sys_symlinkat(const char *target_path, int dirfd, const char *link_path);
struct Symlinkat {};
// int sys_renameat(int olddirfd, const char *old_path, int newdirfd, const char *new_path);
struct Renameat {};
// int sys_fcntl(int fd, int request, va_list args, int *result);
struct Fcntl {};
// int sys_ttyname(int fd, char *buf, size_t size);
struct Ttyname {};
// int sys_fadvise(int fd, off_t offset, off_t length, int advice);
struct Fadvise {};
// void sys_sync();
struct Sync {};
// int sys_fsync(int fd);
struct Fsync {};
// int sys_fdatasync(int fd);
struct Fdatasync {};
// int sys_chmod(const char *pathname, mode_t mode);
struct Chmod {};
// int sys_fchmod(int fd, mode_t mode);
struct Fchmod {};
// int sys_fchmodat(int fd, const char *pathname, mode_t mode, int flags);
struct Fchmodat {};
// int sys_utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags);
struct Utimensat {};
// int sys_mlock(const void *addr, size_t length);
struct Mlock {};
// int sys_munlock(const void *addr, size_t length);
struct Munlock {};
// int sys_mlockall(int flags);
struct Mlockall {};
// int sys_munlockall(void);
struct Munlockall {};
// int sys_mincore(void *addr, size_t length, unsigned char *vec);
struct Mincore {};
// int sys_vm_remap(void *pointer, size_t size, size_t new_size, void **window);
struct VmRemap {};
// int sys_setsid(pid_t *sid);
struct SetSid {};
// int sys_tcgetattr(int fd, struct termios *attr);
struct Tcgetattr {};
// int sys_tcsetattr(int, int, const struct termios *attr);
struct Tcsetattr {};
// int sys_tcsendbreak(int fd, int dur);
struct Tcsendbreak {};
// int sys_tcflow(int, int);
struct Tcflow {};
// int sys_tcflush(int fd, int queue);
struct Tcflush {};
// int sys_tcdrain(int);
struct Tcdrain {};
// int sys_tcgetwinsize(int fd, struct winsize *winsz);
struct Tcgetwinsize {};
// int sys_tcsetwinsize(int fd, const struct winsize *winsz);
struct Tcsetwinsize {};
// int sys_pipe(int *fds, int flags);
struct Pipe {};
// int sys_socketpair(int domain, int type_and_flags, int proto, int *fds);
struct Socketpair {};
// int sys_poll(struct pollfd *fds, nfds_t count, int timeout, int *num_events);
struct Poll {};
// int sys_ppoll(struct pollfd *fds, nfds_t count, const struct timespec *ts, const sigset_t *mask, int *num_events);
struct Ppoll {};
// int sys_ioctl(int fd, unsigned long request, void *arg, int *result);
struct Ioctl {};
// int sys_posix_devctl(int fd, int dcmd, void *__restrict dev_data_ptr, size_t nbyte, int *__restrict dev_info_ptr);
struct PosixDevctl {};
// int sys_getsockopt(int fd, int layer, int number, void *__restrict buffer, socklen_t *__restrict size);
struct GetSockopt {};
// int sys_setsockopt(int fd, int layer, int number, const void *buffer, socklen_t size);
struct SetSockopt {};
// int sys_shutdown(int sockfd, int how);
struct Shutdown {};
// int sys_sockatmark(int sockfd, int *out);
struct Sockatmark {};
// int sys_thread_sigmask(int how, const sigset_t *__restrict set, sigset_t *__restrict retrieve);
struct ThreadSigmask {};
// NOTE: POSIX says that behavior of timeout = nullptr is unspecified. We treat this case
// as an infinite timeout, making sigtimedwait(..., nullptr) equivalent to sigwaitinfo(...)
// int sys_sigtimedwait(const sigset_t *__restrict set, siginfo_t *__restrict info, const struct timespec *__restrict timeout, int *out_signal);
struct Sigtimedwait {};
// int sys_accept(int fd, int *newfd, struct sockaddr *addr_ptr, socklen_t *addr_length, int flags);
struct Accept {};
// int sys_bind(int fd, const struct sockaddr *addr_ptr, socklen_t addr_length);
struct Bind {};
// int sys_connect(int fd, const struct sockaddr *addr_ptr, socklen_t addr_length);
struct Connect {};
// int sys_sockname(int fd, struct sockaddr *addr_ptr, socklen_t max_addr_length, socklen_t *actual_length);
struct Sockname {};
// int sys_peername(int fd, struct sockaddr *addr_ptr, socklen_t max_addr_length, socklen_t *actual_length);
struct Peername {};
// int sys_gethostname(char *buffer, size_t bufsize);
struct GetHostname {};
// int sys_sethostname(const char *buffer, size_t bufsize);
struct SetHostname {};
// int sys_mkfifoat(int dirfd, const char *path, mode_t mode);
struct Mkfifoat {};
// int sys_getentropy(void *buffer, size_t length);
struct GetEntropy {};
// int sys_mknodat(int dirfd, const char *path, int mode, int dev);
struct Mknodat {};
// int sys_umask(mode_t mode, mode_t *old);
struct Umask {};
// int sys_before_cancellable_syscall(ucontext_t *uctx);
struct BeforeCancellableSyscall {};
// int sys_tgkill(int tgid, int tid, int sig);
struct Tgkill {};
// int sys_fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags);
struct Fchownat {};
// int sys_sigaltstack(const stack_t *ss, stack_t *oss);
struct Sigaltstack {};
// int sys_sigsuspend(const sigset_t *set);
struct Sigsuspend {};
// int sys_sigpending(sigset_t *set);
struct Sigpending {};
// int sys_sigqueue(pid_t pid, int sig, const union sigval val);
struct Sigqueue {};
// int sys_setgroups(size_t size, const gid_t *list);
struct SetGroups {};
// int sys_memfd_create(const char *name, int flags, int *fd);
struct MemfdCreate {};
// int sys_madvise(void *addr, size_t length, int advice);
struct Madvise {};
// int sys_posix_madvise(void *addr, size_t length, int advice);
struct PosixMadvise {};
// int sys_msync(void *addr, size_t length, int flags);
struct Msync {};
// int sys_getitimer(int which, struct itimerval *curr_value);
struct GetItimer {};
// int sys_setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
struct SetItimer {};
// int sys_timer_create(clockid_t clk, struct sigevent *__restrict evp, timer_t *__restrict res);
struct TimerCreate {};
// int sys_timer_settime(timer_t t, int flags, const struct itimerspec *__restrict val, struct itimerspec *__restrict old);
struct TimerSettime {};
// int sys_timer_gettime(timer_t t, struct itimerspec *val);
struct TimerGettime {};
// int sys_timer_delete(timer_t t);
struct TimerDelete {};
// int sys_timer_getoverrun(timer_t t, int *out);
struct TimerGetoverrun {};
// int sys_times(struct tms *tms, clock_t *out);
struct Times {};
// int sys_uname(struct utsname *buf);
struct Uname {};
// int sys_pause();
struct Pause {};
// int sys_setresuid(uid_t ruid, uid_t euid, uid_t suid);
struct SetResuid {};
// int sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid);
struct SetResgid {};
// int sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
struct GetResuid {};
// int sys_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);
struct GetResgid {};
// int sys_setreuid(uid_t ruid, uid_t euid);
struct SetReuid {};
// int sys_setregid(gid_t rgid, gid_t egid);
struct SetRegid {};
// int sys_getlogin_r(char *name, size_t name_len);
struct GetLoginR {};
// int sys_if_indextoname(unsigned int index, char *name);
struct IfIndextoname {};
// int sys_if_nametoindex(const char *name, unsigned int *ret);
struct IfNametoindex {};
// int sys_ptsname(int fd, char *buffer, size_t length);
struct Ptsname {};
// int sys_unlockpt(int fd);
struct Unlockpt {};
// int sys_thread_setname(void *tcb, const char *name);
struct ThreadSetname {};
// int sys_thread_getname(void *tcb, char *name, size_t size);
struct ThreadGetname {};
// int sys_sysconf(int num, long *ret);
struct Sysconf {};
// int sys_semget(key_t key, int n, int fl, int *id);
struct Semget {};
// int sys_semctl(int semid, int semnum, int cmd, void *semun, int *ret);
struct Semctl {};
// int sys_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);
struct GetAffinity {};
// int sys_getthreadaffinity(pid_t tid, size_t cpusetsize, cpu_set_t *mask);
struct GetThreadaffinity {};
// int sys_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *mask);
struct SetAffinity {};
// int sys_setthreadaffinity(pid_t tid, size_t cpusetsize, const cpu_set_t *mask);
struct SetThreadaffinity {};
// int sys_get_current_stack_info(void **stack_base, size_t *stack_size);
struct GetCurrentStackInfo {};
// int sys_waitid(idtype_t idtype, id_t id, siginfo_t *info, int options);
struct Waitid {};
// int sys_name_to_handle_at(int dirfd, const char *pathname, struct file_handle *handle, int *mount_id, int flags);
struct NameToHandleAt {};
// int sys_splice(int in_fd, off_t *in_off, int out_fd, off_t *out_off, size_t size, unsigned int flags, ssize_t *out);
struct Splice {};
// int sys_shmat(void **seg_start, int shmid, const void *shmaddr, int shmflg);
struct Shmat {};
// int sys_shmctl(int *idx, int shmid, int cmd, struct shmid_ds *buf);
struct Shmctl {};
// int sys_shmdt(const void *shmaddr);
struct Shmdt {};
// int sys_shmget(int *shm_id, key_t key, size_t size, int shmflg);
struct Shmget {};
// int sys_inet_configured(bool *ipv4, bool *ipv6);
struct InetConfigured {};
// int sys_nice(int nice, int *new_nice);
struct Nice {};
// int sys_openpt(int oflags, int *fd);
struct Openpt {};
// int sys_msgctl(int q, int cmd, struct msqid_ds *buf);
struct Msgctl {};
// int sys_msgget(key_t k, int flag, int *out);
struct Msgget {};
// int sys_msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg, ssize_t *out);
struct Msgrcv {};
// int sys_msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
struct Msgsnd {};
// int sys_mq_open(const char *name, int oflag, mode_t mode, mq_attr *attr, mqd_t *out);
struct MqOpen {};
// int sys_mq_unlink(const char *name);
struct MqUnlink {};
// int sys_mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);
struct MqReceive {};
// int sys_mq_get_attr(mqd_t mqdes, mq_attr *mqstat);
struct MqGetAttr {};
// int sys_mq_set_attr(mqd_t mqdes, mq_attr *mqstat, mq_attr *omqstat);
struct MqSetAttr {};
#endif // __MLIBC_POSIX_OPTION
#if __MLIBC_LINUX_OPTION
#include <ifaddrs.h>
#include <sched.h>
#include <stdarg.h>
#include <sys/sysinfo.h>
#include <sys/statfs.h>
#include <poll.h>
#include <linux/capability.h>
#include <abi-bits/pid_t.h>
#include <abi-bits/mode_t.h>
#include <abi-bits/statx.h>
#include <bits/off_t.h>
#include <bits/ssize_t.h>
#include <bits/size_t.h>
// int sys_inotify_create(int flags, int *fd);
struct InotifyCreate {};
// int sys_inotify_add_watch(int ifd, const char *path, uint32_t mask, int *wd);
struct InotifyAddWatch {};
// int sys_inotify_rm_watch(int ifd, int wd);
struct InotifyRmWatch {};
// int sys_mount(const char *source, const char *target, const char *fstype, unsigned long flags, const void *data);
struct Mount {};
// int sys_umount2(const char *target, int flags);
struct Umount2 {};
// int sys_ptrace(long req, pid_t pid, void *addr, void *data, long *out);
struct Ptrace {};
// int sys_capget(cap_user_header_t hdrp, cap_user_data_t datap);
struct Capget {};
// int sys_capset(cap_user_header_t hdrp, const cap_user_data_t datap);
struct Capset {};
// int sys_prctl(int option, va_list va, int *out);
struct Prctl {};
// int sys_init_module(void *module, unsigned long length, const char *args);
struct InitModule {};
// int sys_delete_module(const char *name, unsigned flags);
struct DeleteModule {};
// int sys_klogctl(int type, char *bufp, int len, int *out);
struct Klogctl {};
// int sys_getcpu(int *cpu);
struct Getcpu {};
// int sys_sysinfo(struct sysinfo *info);
struct Sysinfo {};
// int sys_swapon(const char *path, int flags);
struct Swapon {};
// int sys_swapoff(const char *path);
struct Swapoff {};
// int sys_setxattr(const char *path, const char *name, const void *val, size_t size, int flags);
struct Setxattr {};
// int sys_lsetxattr(const char *path, const char *name, const void *val, size_t size, int flags);
struct Lsetxattr {};
// int sys_fsetxattr(int fd, const char *name, const void *val, size_t size, int flags);
struct Fsetxattr {};
// int sys_getxattr(const char *path, const char *name, void *val, size_t size, ssize_t *nread);
struct Getxattr {};
// int sys_lgetxattr(const char *path, const char *name, void *val, size_t size, ssize_t *nread);
struct Lgetxattr {};
// int sys_fgetxattr(int fd, const char *name, void *val, size_t size, ssize_t *nread);
struct Fgetxattr {};
// int sys_listxattr(const char *path, char *list, size_t size, ssize_t *nread);
struct Listxattr {};
// int sys_llistxattr(const char *path, char *list, size_t size, ssize_t *nread);
struct Llistxattr {};
// int sys_flistxattr(int fd, char *list, size_t size, ssize_t *nread);
struct Flistxattr {};
// int sys_removexattr(const char *path, const char *name);
struct Removexattr {};
// int sys_lremovexattr(const char *path, const char *name);
struct Lremovexattr {};
// int sys_fremovexattr(int fd, const char *name);
struct Fremovexattr {};
// int sys_statfs(const char *path, struct statfs *buf);
struct Statfs {};
// int sys_fstatfs(int fd, struct statfs *buf);
struct Fstatfs {};
// int sys_statx(int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf);
struct Statx {};
// int sys_getifaddrs(struct ifaddrs **);
struct Getifaddrs {};
// int sys_sendfile(int outfd, int infd, off_t *offset, size_t count, ssize_t *out);
struct Sendfile {};
// int sys_syncfs(int fd);
struct Syncfs {};
// int sys_unshare(int flags);
struct Unshare {};
// int sys_setns(int fd, int nstype);
struct SetNs {};
// int sys_pidfd_open(pid_t pid, unsigned int flags, int *outfd);
struct PidfdOpen {};
// int sys_pidfd_getpid(int fd, pid_t *outpid);
struct PidfdGetpid {};
// int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info, unsigned int flags);
struct PidfdSendSignal {};
// int sys_process_vm_readv(pid_t pid, const struct iovec *local_iov, unsigned long liovcnt, const struct iovec *remote_iov, unsigned long riovcnt, unsigned long flags, ssize_t *out);
struct ProcessVmReadv {};
// int sys_process_vm_writev(pid_t pid, const struct iovec *local_iov, unsigned long liovcnt, const struct iovec *remote_iov, unsigned long riovcnt, unsigned long flags, ssize_t *out);
struct ProcessVmWritev {};
// int sys_fsopen(const char *fsname, unsigned int flags, int *outfd);
struct Fsopen {};
// int sys_fsmount(int fsfd, unsigned int flags, unsigned int mountflags, int *outfd);
struct Fsmount {};
// int sys_fsconfig(int fd, unsigned int cmd, const char *key, const void *val, int aux);
struct Fsconfig {};
// int sys_move_mount(int from_dirfd, const char *from_path, int to_dirfd, const char *to_path, unsigned int flags);
struct MoveMount {};
// int sys_open_tree(int dirfd, const char *path, unsigned int flags, int *outfd);
struct OpenTree {};
// int sys_copy_file_range(int fd_in, off_t *off_in, int fd_out, off_t *off_out, size_t count, unsigned int flags, ssize_t *bytes_copied);
struct CopyFileRange {};
#endif // __MLIBC_LINUX_OPTION
#if __MLIBC_LINUX_EPOLL_OPTION
#include <sys/epoll.h>
// int sys_epoll_create(int flags, int *fd);
struct EpollCreate {};
// int sys_epoll_ctl(int epfd, int mode, int fd, struct epoll_event *ev);
struct EpollCtl {};
// int sys_epoll_pwait(int epfd, struct epoll_event *ev, int n, int timeout, const sigset_t *sigmask, int *raised);
struct EpollPwait {};
#endif // __MLIBC_LINUX_EPOLL_OPTION
#if __MLIBC_LINUX_TIMERFD_OPTION
#include <sys/timerfd.h>
// int sys_timerfd_create(int clockid, int flags, int *fd);
struct TimerfdCreate {};
// int sys_timerfd_settime(int fd, int flags, const struct itimerspec *value, struct itimerspec *oldvalue);
struct TimerfdSettime {};
// int sys_timerfd_gettime(int fd, struct itimerspec *its);
struct TimerfdGettime {};
#endif // __MLIBC_LINUX_TIMERFD_OPTION
#if __MLIBC_LINUX_SIGNALFD_OPTION
#include <sys/signalfd.h>
// int sys_signalfd_create(const sigset_t *, int flags, int *fd);
struct SignalfdCreate {};
#endif // __MLIBC_LINUX_SIGNALFD_OPTION
#if __MLIBC_LINUX_EVENTFD_OPTION
#include <sys/eventfd.h>
// int sys_eventfd_create(unsigned int initval, int flags, int *fd);
struct EventfdCreate {};
#endif // __MLIBC_LINUX_EVENTFD_OPTION
#if __MLIBC_LINUX_REBOOT_OPTION
#include <sys/reboot.h>
// int sys_reboot(int cmd);
struct Reboot {};
#endif // __MLIBC_LINUX_REBOOT_OPTION
#if __MLIBC_LINUX_WRAPPERS_OPTION
// int sys_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *uinfo);
struct RtSigqueueinfo {};
// int sys_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *uinfo);
struct RtTgSigqueueinfo {};
#endif // __MLIBC_LINUX_WRAPPERS_OPTION
#endif /* MLIBC_SYSDEP_TAGS */
@@ -0,0 +1,189 @@
#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_RTLD
// 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 RTLD 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);
// options/linker/x86_64/runtime.S uses the offset of dtvPointers.
static_assert(offsetof(Tcb, dtvPointers) == 16);
#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);
#elif defined (__m68k__)
// The thread pointer on m68k points to 0x7000 bytes *after* the end of the
// TCB, so similarly to as on RISC-V, we need to keep the value in
// sysdeps/linux/m68k/cp_syscall.S up-to-date.
static_assert(sizeof(Tcb) - offsetof(Tcb, cancelBits) == 0x30);
#elif defined(__loongarch64)
static_assert(sizeof(Tcb) - offsetof(Tcb, cancelBits) == 96);
#else
#error "Missing architecture specific code."
#endif
@@ -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);
}
@@ -0,0 +1,18 @@
#pragma once
#include <mlibc/thread.hpp>
#include <mlibc/internal-sysdeps.hpp>
namespace mlibc {
inline unsigned int this_tid() {
// During RTLD 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;
}
}
}
@@ -0,0 +1,115 @@
#pragma once
#include <abi-bits/errno.h>
#include <abi-bits/pid_t.h>
#include <abi-bits/seek-whence.h>
#include <abi-bits/utmp-defines.h>
#include <bits/ensure.h>
#include <bits/posix/timeval.h>
#include <bits/size_t.h>
#include <mlibc/ansi-sysdeps.hpp>
#include <string.h>
#include <type_traits>
namespace mlibc {
template <class S>
concept UtmpStruct = requires(S s) {
std::is_same_v<decltype(s.ut_user), char[]>;
std::is_same_v<decltype(s.ut_id), char[]>;
std::is_same_v<decltype(s.ut_line), char[]>;
std::is_same_v<decltype(s.ut_pid), pid_t>;
std::is_same_v<decltype(s.ut_type), short int>;
std::is_same_v<decltype(s.ut_tv), struct timeval>;
};
template <UtmpStruct U>
int getUtmpEntry(int fd, U *res) {
ssize_t progress = 0;
ssize_t read = 0;
char *ptr = reinterpret_cast<char *>(res);
int err = mlibc::sys_read(fd, ptr, sizeof(U), &read);
if(err)
return err;
if(read == sizeof(U))
return 0;
else if(read == 0)
return ESRCH;
progress = read;
while(read) {
err = mlibc::sys_read(fd, ptr + progress, sizeof(U) - progress, &read);
if(err)
return err;
progress += read;
if(progress == sizeof(U))
return 0;
}
return ESRCH;
}
template <UtmpStruct U>
int getUtmpEntryById(int fd, const U *id, U *res) {
while(true) {
U tmp;
if(int e = getUtmpEntry(fd, &tmp); e)
return e;
if(id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
|| id->ut_type == NEW_TIME || id->ut_type == OLD_TIME) {
if(tmp.ut_type == id->ut_type) {
memcpy(res, &tmp, sizeof(U));
return 0;
}
} else if(id->ut_type == INIT_PROCESS || id->ut_type == LOGIN_PROCESS
|| id->ut_type == USER_PROCESS || id->ut_type == DEAD_PROCESS) {
if(!memcmp(tmp.ut_id, id->ut_id, sizeof(U::ut_id))) {
memcpy(res, &tmp, sizeof(U));
return 0;
}
}
}
}
template <UtmpStruct U>
int getUtmpEntryByType(int fd, const U *id, U *res) {
while(true) {
U tmp;
if(int e = getUtmpEntry(fd, &tmp); e)
return e;
if(id->ut_type == USER_PROCESS || id->ut_type == LOGIN_PROCESS) {
if(!strncmp(tmp.ut_line, id->ut_line, sizeof(U::ut_line))) {
memcpy(res, &tmp, sizeof(U));
return 0;
}
}
}
}
template <UtmpStruct U>
int putUtmpEntry(int fd, const U *ut) {
size_t progress = 0;
char *ptr = (char *) ut;
off_t discard;
if(int e = mlibc::sys_seek(fd, 0, SEEK_END, &discard); e)
return e;
while(progress < sizeof(U)) {
ssize_t written = 0;
if(int e = mlibc::sys_write(fd, ptr + progress, sizeof(U) - progress, &written); e)
return e;
progress += written;
}
return 0;
}
} // namespace mlibc
@@ -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 */
@@ -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
@@ -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 LoongArch, 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 volatile ("move %0, $sp" : "=r"(sp));
return sp;
}
} // namespace mlibc
@@ -0,0 +1,67 @@
.global feclearexcept
.type feclearexcept, %function
feclearexcept:
li.w $t0, 0x1f0000
and $a0, $a0, $t0
movfcsr2gr $t1, $fcsr0
andn $t1, $t1, $a0
movgr2fcsr $fcsr0, $t1
li.w $a0, 0
jr $ra
.global feraiseexcept
.type feraiseexcept, %function
feraiseexcept:
li.w $t0, 0x1f0000
and $a0, $a0, $t0
movfcsr2gr $t1, $fcsr0
or $t1, $t1, $a0
movgr2fcsr $fcsr0, $t1
li.w $a0, 0
jr $ra
.global fetestexcept
.type fetestexcept, %function
fetestexcept:
li.w $t0, 0x1f0000
and $a0, $a0, $t0
movfcsr2gr $t1, $fcsr0
and $a0, $t1, $a0
jr $ra
.global fegetround
.type fegetround, %function
fegetround:
movfcsr2gr $t0, $fcsr0
andi $a0, $t0, 0x300
jr $ra
.global __fesetround
.type __fesetround, %function
__fesetround:
li.w $t0, 0x300
and $a0, $a0, $t0
movfcsr2gr $t1, $fcsr0
andn $t1, $t1, $t0
or $t1, $t1, $a0
movgr2fcsr $fcsr0, $t1
li.w $a0, 0
jr $ra
.global fegetenv
.type fegetenv, %function
fegetenv:
movfcsr2gr $t0, $fcsr0
st.w $t0, $a0, 0
li.w $a0, 0
jr $ra
.global fesetenv
.type fesetenv, %function
fesetenv:
addi.d $t0, $a0, 1
beq $t0, $r0, 1f
ld.w $t0, $a0, 0
1: movgr2fcsr $fcsr0, $t0
li.w $a0, 0
jr $ra
@@ -0,0 +1,68 @@
.global setjmp
.type setjmp, "function"
.global _setjmp
.type _setjmp, "function"
setjmp:
_setjmp:
st.d $ra, $a0, 0
st.d $sp, $a0, 8
st.d $r21, $a0, 16
st.d $fp, $a0, 24
st.d $s0, $a0, 32
st.d $s1, $a0, 40
st.d $s2, $a0, 48
st.d $s3, $a0, 56
st.d $s4, $a0, 64
st.d $s5, $a0, 72
st.d $s6, $a0, 80
st.d $s7, $a0, 88
st.d $s8, $a0, 96
fst.d $fs0, $a0, 104
fst.d $fs1, $a0, 112
fst.d $fs2, $a0, 120
fst.d $fs3, $a0, 128
fst.d $fs4, $a0, 136
fst.d $fs5, $a0, 144
fst.d $fs6, $a0, 152
fst.d $fs7, $a0, 160
move $a0, $r0
ret
.global sigsetjmp
.type sigsetjmp, "function"
sigsetjmp:
break 0 // TODO
.global longjmp
.type longjmp, "function"
.global _longjmp
.type _longjmp, "function"
longjmp:
_longjmp:
ld.d $ra, $a0, 0
ld.d $sp, $a0, 8
ld.d $r21, $a0, 16
ld.d $fp, $a0, 24
ld.d $s0, $a0, 32
ld.d $s1, $a0, 40
ld.d $s2, $a0, 48
ld.d $s3, $a0, 56
ld.d $s4, $a0, 64
ld.d $s5, $a0, 72
ld.d $s6, $a0, 80
ld.d $s7, $a0, 88
ld.d $s8, $a0, 96
fld.d $fs0, $a0, 104
fld.d $fs1, $a0, 112
fld.d $fs2, $a0, 120
fld.d $fs3, $a0, 128
fld.d $fs4, $a0, 136
fld.d $fs5, $a0, 144
fld.d $fs6, $a0, 152
fld.d $fs7, $a0, 160
sltui $a0, $a1, 1
add.d $a0, $a0, $a1
jr $ra
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,13 @@
#ifndef MLIBC_ARCH_DEFS_HPP
#define MLIBC_ARCH_DEFS_HPP
#include <stddef.h>
namespace mlibc {
// not strictly true, can be 4 or 8k on 68040/68060, and many more on others
inline constexpr size_t page_size = 0x1000;
} // namespace mlibc
#endif // MLIBC_ARCH_DEFS_HPP
@@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
#include <mlibc/internal-sysdeps.hpp>
#include <mlibc/tcb.hpp>
#include <bits/ensure.h>
namespace mlibc {
extern "C" void *__m68k_read_tp();
inline Tcb *get_current_tcb() {
// On m68k, the end of the TCB is 0x7000 below the thread pointer.
void *ptr = __m68k_read_tp();
return reinterpret_cast<Tcb *>((uintptr_t)ptr - 0x7000 - sizeof(Tcb));
}
inline uintptr_t get_sp() {
uintptr_t sp;
asm volatile ("move.l %%sp, %0" : "=r"(sp));
return sp;
}
} // namespace mlibc
@@ -0,0 +1,111 @@
#include <bits/ansi/fenv.h>
.global feclearexcept
.type feclearexcept,@function
feclearexcept:
move.l 4(%sp), %d0
andi.l #~FE_ALL_EXCEPT, %d0
bne 1f
fmove.l %fpsr, %d1
movel 4(%sp), %d0
not.l %d0
and.l %d0, %d1
fmove.l %d1, %fpsr
moveq.l #0, %d0
rts
1:
moveq.l #-1, %d0
rts
.global feraiseexcept
.type feraiseexcept,@function
feraiseexcept:
move.l 4(%sp), %d0
andi.l #~FE_ALL_EXCEPT, %d0
bne 1f
fmove.l %fpsr, %d1
or.l 4(%sp), %d1
fmove.l %d1, %fpsr
moveq.l #0, %d0
rts
1:
moveq.l #-1, %d0
rts
.global __fesetround
.hidden __fesetround
.type __fesetround,@function
__fesetround:
fmove.l %fpcr, %d1
andi.l #~FE_UPWARD, %d1
or.l 4(%sp), %d1
fmove.l %d1, %fpcr
moveq.l #0, %d0
rts
.global fegetround
.type fegetround,@function
fegetround:
fmove.l %fpcr, %d0
andi.l #FE_UPWARD, %d0
rts
.global fegetenv
.type fegetenv,@function
fegetenv:
move.l 4(%sp), %a0
fmove.l %fpcr, %d0
move.l %d0, (%a0)
fmove.l %fpsr, %d0
move.l %d0, 4(%a0)
fmove.l %fpiar, %d0
move.l %d0, 8(%a0)
moveq.l #0, %d0
rts
.global fesetenv
.type fesetenv,@function
fesetenv:
move.l 4(%sp), %a0
cmp.l #-1, %a0
beq 1f
move.l (%a0), %d0
fmove.l %d0, %fpcr
move.l 4(%a0), %d0
fmove.l %d0, %fpsr
move.l 8(%a0), %d0
fmove.l %d0, %fpiar
moveq.l #0, %d0
rts
1:
clr.l %d0
fmove.l %d0, %fpcr
fmove.l %d0, %fpsr
fmove.l %d0, %fpiar
moveq.l #0, %d0
rts
.global fetestexcept
.type fetestexcept,@function
fetestexcept:
fmove.l %fpsr, %d0
and.l 4(%sp), %d0
rts
.section .note.GNU-stack,"",%progbits
@@ -0,0 +1,52 @@
.type __setjmp, "function"
__setjmp:
movea.l 4(%sp), %a0
movem.l %d2-%d7/%a2-%a7, (%a0)
move.l (%sp), 48(%a0)
tst.l %d0
bne 1f
clr.l %d0
rts
1:
move.l #1, -(%sp)
move.l %a0, -(%sp)
jbsr __sigsetjmp@PLTPC
addq.l #8, %sp
rts
.global setjmp
.type setjmp, "function"
.global _setjmp
.type _setjmp, "function"
setjmp:
_setjmp:
clr.l %d0
jmp __setjmp
.global sigsetjmp
.type sigsetjmp, "function"
sigsetjmp:
move.l #1, %d0
jmp __setjmp
.global longjmp
.type longjmp, "function"
.global _longjmp
.type _longjmp, "function"
longjmp:
_longjmp:
movea.l 4(%sp),%a0
move.l 8(%sp),%d0
bne 1f
move.l #1,%d0
1:
movem.l (%a0), %d2-%d7/%a2-%a7
move.l 48(%a0), (%sp)
rts
.section .note.GNU-stack,"",%progbits
@@ -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
@@ -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 volatile ("mv %0, sp" : "=r"(sp));
return sp;
}
} // namespace mlibc
@@ -0,0 +1,22 @@
#ifndef _MLIBC_SYS_HWPROBE_H
#define _MLIBC_SYS_HWPROBE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <abi-bits/riscv-hwprobe.h>
#include <bits/cpu_set.h>
#include <bits/size_t.h>
int __riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count, size_t cpusetsize, cpu_set_t *cpus,
unsigned int flags);
typedef int (*__riscv_hwprobe_t)(struct riscv_hwprobe *pairs, size_t pair_count,
size_t cpusetsize, cpu_set_t *cpus, unsigned int flags);
#ifdef __cplusplus
}
#endif
#endif /* _MLIBC_SYS_HWPROBE_H */
@@ -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
@@ -0,0 +1,18 @@
#include <abi-bits/errno.h>
#include <bits/ensure.h>
#include <mlibc/internal-sysdeps.hpp>
#include <sys/hwprobe.h>
int __riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count, size_t cpusetsize, cpu_set_t *cpus, unsigned int flags) {
if (!mlibc::sys_riscv_hwprobe) {
MLIBC_MISSING_SYSDEP();
return -ENOSYS;
}
int ret = mlibc::sys_riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags);
if (ret)
return -ret;
return 0;
}
@@ -0,0 +1,77 @@
.global setjmp
.type setjmp, "function"
.global _setjmp
.type _setjmp, "function"
setjmp:
_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"
.global _longjmp
.type _longjmp, "function"
longjmp:
_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
@@ -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
@@ -0,0 +1,21 @@
#pragma once
#include <stdint.h>
#include <mlibc/tcb.hpp>
namespace mlibc {
inline Tcb *get_current_tcb() {
uintptr_t ptr;
asm volatile ("movl %%gs:0, %0" : "=r"(ptr));
return reinterpret_cast<Tcb *>(ptr);
}
inline uintptr_t get_sp() {
uintptr_t esp;
asm volatile ("mov %%esp, %0" : "=r"(esp));
return esp;
}
} // namespace mlibc
@@ -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
@@ -0,0 +1,59 @@
.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"
.global _setjmp
.type _setjmp, "function"
setjmp:
_setjmp:
xor %edx, %edx
jmp __setjmp
.global sigsetjmp
.type sigsetjmp, "function"
sigsetjmp:
mov $1, %edx
jmp __setjmp
.global longjmp
.type longjmp, "function"
.global _longjmp
.type _longjmp, "function"
longjmp:
_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
@@ -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
@@ -0,0 +1,20 @@
#pragma once
#include <stdint.h>
#include <mlibc/tcb.hpp>
namespace mlibc {
inline Tcb *get_current_tcb() {
uintptr_t ptr;
asm volatile ("movq %%fs:0, %0" : "=r"(ptr));
return reinterpret_cast<Tcb *>(ptr);
}
inline uintptr_t get_sp() {
uintptr_t rsp;
asm volatile ("mov %%rsp, %0" : "=r"(rsp));
return rsp;
}
} // namespace mlibc
@@ -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
@@ -0,0 +1,60 @@
.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"
.global _setjmp
.type _setjmp, "function"
setjmp:
_setjmp:
xor %rdx, %rdx
jmp __setjmp
.global sigsetjmp
.type sigsetjmp, "function"
sigsetjmp:
mov $1, %rdx
jmp __setjmp
.global longjmp
.type longjmp, "function"
.global _longjmp
.type _longjmp, "function"
longjmp:
_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