#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __MLIBC_POSIX_OPTION #include #endif // __MLIBC_POSIX_OPTION extern "C" int __cxa_atexit(void (*function)(void *), void *argument, void *dso_tag); void __mlibc_do_finalize(); namespace { // According to the first paragraph of [C11 7.22.7], // mblen(), mbtowc() and wctomb() have an internal state. // The string functions mbstowcs() and wcstombs() do *not* have this state. thread_local __mlibc_mbstate mblen_state = __MLIBC_MBSTATE_INITIALIZER; thread_local __mlibc_mbstate mbtowc_state = __MLIBC_MBSTATE_INITIALIZER; } double atof(const char *string) { return strtod(string, NULL); } int atoi(const char *string) { return strtol(string, nullptr, 10); } long atol(const char *string) { return strtol(string, nullptr, 10); } long long atoll(const char *string) { return strtoll(string, nullptr, 10); } // POSIX extensions but are here for simplicities sake. Forward declaration is here // to avoid exporting sigprocmask when posix is disabled. int sigprocmask(int, const sigset_t *__restrict, sigset_t *__restrict); extern "C" { __attribute__((__returns_twice__)) int __sigsetjmp(sigjmp_buf buffer, int savesigs) { buffer[0].savesigs = savesigs; if (savesigs) sigprocmask(0, NULL, &buffer[0].sigset); return 0; } } __attribute__((__noreturn__)) void siglongjmp(sigjmp_buf buffer, int value) { if (buffer[0].savesigs) sigprocmask(SIG_SETMASK, &buffer[0].sigset, NULL); jmp_buf b; b[0].reg_state = buffer[0].reg_state; longjmp(b, value); } double strtod(const char *__restrict string, char **__restrict end) { return mlibc::strtofp(string, end); } float strtof(const char *__restrict string, char **__restrict end) { return mlibc::strtofp(string, end); } long double strtold(const char *__restrict string, char **__restrict end) { return mlibc::strtofp(string, end); } long strtol(const char *__restrict string, char **__restrict end, int base) { return mlibc::stringToInteger(string, end, base); } long long strtoll(const char *__restrict string, char **__restrict end, int base) { return mlibc::stringToInteger(string, end, base); } unsigned long strtoul(const char *__restrict string, char **__restrict end, int base) { return mlibc::stringToInteger(string, end, base); } unsigned long long strtoull(const char *__restrict string, char **__restrict end, int base) { return mlibc::stringToInteger(string, end, base); } frg::mt19937 __mlibc_rand_engine; int rand() { // rand() is specified to return a positive number so we discard the MSB. return static_cast(__mlibc_rand_engine() & 0x7FFFFFFF); } static unsigned temper(unsigned x) { x ^= x >> 11; x ^= x << 7 & 0x9D2C5680; x ^= x << 15 & 0xEFC60000; x ^= x >> 18; return x; } int rand_r(unsigned *seed) { return temper(*seed = *seed * 1103515245 + 12345) / 2; } void srand(unsigned int s) { __mlibc_rand_engine.seed(s); } void *aligned_alloc(size_t alignment, size_t size) { void *ptr; // alignment must be a power of two, and size % alignment must be 0 if (alignment & (alignment - 1) || size & (alignment - 1)) { errno = EINVAL; return nullptr; } // posix_memalign requires that the alignment is a multiple of sizeof(void *) if (alignment < sizeof(void *)) alignment = sizeof(void *); int ret = posix_memalign(&ptr, alignment, size); if (ret) { errno = ret; return nullptr; } return ptr; } void *calloc(size_t count, size_t size) { // we want to ensure that count*size > SIZE_MAX doesn't happen // to prevent overflowing, we divide both sides of the inequality by size and check with that if(size && count > (SIZE_MAX / size)) { errno = EINVAL; return NULL; } // TODO: this could be done more efficient if the OS gives us already zero'd pages void *ptr = malloc(count * size); if(!ptr) return nullptr; memset(ptr, 0, count * size); return ptr; } // free() is provided by the platform // malloc() is provided by the platform // realloc() is provided by the platform void abort(void) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGABRT); if (mlibc::sys_sigprocmask) { mlibc::sys_sigprocmask(SIG_UNBLOCK, &set, nullptr); } raise(SIGABRT); sigfillset(&set); sigdelset(&set, SIGABRT); if (mlibc::sys_sigprocmask) { mlibc::sys_sigprocmask(SIG_SETMASK, &set, nullptr); } struct sigaction sa; sa.sa_handler = SIG_DFL; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); if (mlibc::sys_sigaction(SIGABRT, &sa, nullptr)) mlibc::panicLogger() << "mlibc: sigaction failed in abort" << frg::endlog; if (raise(SIGABRT)) mlibc::panicLogger() << "mlibc: raise failed in abort" << frg::endlog; __builtin_trap(); } int atexit(void (*func)(void)) { // TODO: the function pointer types are not compatible; // the conversion here is undefined behavior. its fine to do // this on the x86_64 abi though. __cxa_atexit((void (*) (void *))func, nullptr, nullptr); return 0; } int at_quick_exit(void (*func)(void)) { (void)func; __ensure(!"Not implemented"); __builtin_unreachable(); } void exit(int status) { __mlibc_do_finalize(); mlibc::sys_exit(status); } void _Exit(int status) { mlibc::sys_exit(status); } // getenv() is provided by POSIX void quick_exit(int) { __ensure(!"Not implemented"); __builtin_unreachable(); } extern char **environ; int system(const char *command) { int status = -1; pid_t child; MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fork && mlibc::sys_waitpid && mlibc::sys_execve && mlibc::sys_sigprocmask && mlibc::sys_sigaction, -1); #if __MLIBC_POSIX_OPTION pthread_testcancel(); #endif // __MLIBC_POSIX_OPTION if (!command) { return 1; } struct sigaction new_sa, old_int, old_quit; sigset_t new_mask, old_mask; new_sa.sa_handler = SIG_IGN; new_sa.sa_flags = 0; sigemptyset(&new_sa.sa_mask); mlibc::sys_sigaction(SIGINT, &new_sa, &old_int); mlibc::sys_sigaction(SIGQUIT, &new_sa, &old_quit); sigemptyset(&new_mask); sigaddset(&new_mask, SIGCHLD); mlibc::sys_sigprocmask(SIG_BLOCK, &new_mask, &old_mask); if (int e = mlibc::sys_fork(&child)) { errno = e; } else if (!child) { mlibc::sys_sigaction(SIGINT, &old_int, nullptr); mlibc::sys_sigaction(SIGQUIT, &old_quit, nullptr); mlibc::sys_sigprocmask(SIG_SETMASK, &old_mask, nullptr); const char *args[] = { "sh", "-c", command, nullptr }; mlibc::sys_execve("/bin/sh", const_cast(args), environ); _Exit(127); } else { int err; pid_t unused; while ((err = mlibc::sys_waitpid(child, &status, 0, NULL, &unused)) < 0) { if (err == EINTR) continue; errno = err; status = -1; } } mlibc::sys_sigaction(SIGINT, &old_int, nullptr); mlibc::sys_sigaction(SIGQUIT, &old_quit, nullptr); mlibc::sys_sigprocmask(SIG_SETMASK, &old_mask, nullptr); return status; } char *mktemp(char *) { __ensure(!"Not implemented"); __builtin_unreachable(); } void *bsearch(const void *key, const void *base, size_t count, size_t size, int (*compare)(const void *, const void *)) { // Invariant: Element is in the interval [i, j). size_t i = 0; size_t j = count; while(i < j) { size_t k = (j - i) / 2; auto element = reinterpret_cast(base) + (i + k) * size; auto res = compare(key, element); if(res < 0) { j = i + k; }else if(res > 0) { i = i + k + 1; }else{ return const_cast(element); } } __ensure(i == j); return nullptr; } static int qsort_callback(const void *a, const void *b, void *arg) { auto compare = reinterpret_cast(arg); return compare(a, b); } void qsort(void *base, size_t count, size_t size, int (*compare)(const void *, const void *)) { return qsort_r(base, count, size, qsort_callback, (void *) compare); } void qsort_r(void *base, size_t count, size_t size, int (*compare)(const void *, const void *, void *), void *arg) { // TODO: implement a faster sort for(size_t i = 0; i < count; i++) { void *u = (void *)((uintptr_t)base + i * size); for(size_t j = i + 1; j < count; j++) { void *v = (void *)((uintptr_t)base + j * size); if(compare(u, v, arg) <= 0) continue; // swap u and v char *u_bytes = (char *)u; char *v_bytes = (char *)v; for(size_t k = 0; k < size; k++) { char temp = u_bytes[k]; u_bytes[k] = v_bytes[k]; v_bytes[k] = temp; } } } } int abs(int num) { return num < 0 ? -num : num; } long labs(long num) { return num < 0 ? -num : num; } long long llabs(long long num) { return num < 0 ? -num : num; } div_t div(int number, int denom) { div_t r; r.quot = number / denom; r.rem = number % denom; return r; } ldiv_t ldiv(long number, long denom) { ldiv_t r; r.quot = number / denom; r.rem = number % denom; return r; } lldiv_t lldiv(long long number, long long denom) { lldiv_t r; r.quot = number / denom; r.rem = number % denom; return r; } int mblen(const char *mbs, size_t mb_limit) { auto cc = mlibc::current_charcode(); wchar_t wc; mlibc::code_seq nseq{mbs, mbs + mb_limit}; mlibc::code_seq wseq{&wc, &wc + 1}; if(!mbs) { mblen_state = __MLIBC_MBSTATE_INITIALIZER; return cc->has_shift_states; } if(auto e = cc->decode_wtranscode(nseq, wseq, mblen_state); e != mlibc::charcode_error::null) __ensure(!"decode_wtranscode() errors are not handled"); return nseq.it - mbs; } int mbtowc(wchar_t *__restrict wc, const char *__restrict mb, size_t max_size) { auto cc = mlibc::current_charcode(); __ensure(max_size); // If wc is NULL, decode into a single local character which we discard // to obtain the length. wchar_t tmp_wc; if (!wc) wc = &tmp_wc; if (mb) { if (*mb) { mlibc::code_seq wseq{wc, wc + 1}; mlibc::code_seq nseq{mb, mb + max_size}; auto e = cc->decode_wtranscode(nseq, wseq, mbtowc_state); switch(e) { // We keep the state, so we can simply return here. case mlibc::charcode_error::input_underflow: case mlibc::charcode_error::null: { return nseq.it - mb; } case mlibc::charcode_error::illegal_input: { errno = -EILSEQ; return -1; } case mlibc::charcode_error::dirty: { mlibc::panicLogger() << "decode_wtranscode() charcode_error::dirty errors are not handled" << frg::endlog; break; } case mlibc::charcode_error::output_overflow: { mlibc::panicLogger() << "decode_wtranscode() charcode_error::output_overflow errors are not handled" << frg::endlog; break; } } __builtin_unreachable(); } else { *wc = L'\0'; return 0; // When mbs is a null byte, return 0 } } else { mblen_state = __MLIBC_MBSTATE_INITIALIZER; return cc->has_shift_states; } } int wctomb(char *, wchar_t) { __ensure(!"Not implemented"); __builtin_unreachable(); } size_t mbstowcs(wchar_t *wcs, const char *mbs, size_t wc_limit) { auto cc = mlibc::current_charcode(); __mlibc_mbstate st = __MLIBC_MBSTATE_INITIALIZER; mlibc::code_seq nseq{mbs, nullptr}; mlibc::code_seq wseq{wcs, wcs + wc_limit}; if(!wcs) { size_t size; if(auto e = cc->decode_wtranscode_length(nseq, &size, st); e != mlibc::charcode_error::null) __ensure(!"decode_wtranscode() errors are not handled"); return size; } if(auto e = cc->decode_wtranscode(nseq, wseq, st); e != mlibc::charcode_error::null) { __ensure(!"decode_wtranscode() errors are not handled"); __builtin_unreachable(); }else{ size_t n = wseq.it - wcs; if(n < wc_limit) // Null-terminate resulting wide string. wcs[n] = 0; return n; } } size_t wcstombs(char *mb_string, const wchar_t *wc_string, size_t max_size) { return wcsrtombs(mb_string, &wc_string, max_size, 0); } void free(void *ptr) { // TODO: Print PID only if POSIX option is enabled. if (mlibc::globalConfig().debugMalloc) { mlibc::infoLogger() << "mlibc (PID ?): free() on " << ptr << frg::endlog; if((uintptr_t)ptr & 1) mlibc::infoLogger() << __builtin_return_address(0) << frg::endlog; } getAllocator().free(ptr); } void *malloc(size_t size) { auto nptr = getAllocator().allocate(size); // TODO: Print PID only if POSIX option is enabled. if (mlibc::globalConfig().debugMalloc) mlibc::infoLogger() << "mlibc (PID ?): malloc() returns " << nptr << frg::endlog; return nptr; } void *realloc(void *ptr, size_t size) { auto nptr = getAllocator().reallocate(ptr, size); // TODO: Print PID only if POSIX option is enabled. if (mlibc::globalConfig().debugMalloc) mlibc::infoLogger() << "mlibc (PID ?): realloc() on " << ptr << " returns " << nptr << frg::endlog; return nptr; } int posix_memalign(void **out, size_t align, size_t size) { if(align < sizeof(void *)) return EINVAL; if(align & (align - 1)) // Make sure that align is a power of two. return EINVAL; auto p = getAllocator().allocate(frg::max(align, size)); if(!p) return ENOMEM; // Hope that the alignment was respected. This works on the current allocator. // TODO: Make the allocator alignment-aware. __ensure(!(reinterpret_cast(p) & (align - 1))); *out = p; return 0; }