Newer
Older
/*
* Copyright (C) 2013 Cloudius Systems, Ltd.
*
* This work is open source software, licensed under the terms of the
* BSD license as described in the LICENSE file in the top-level directory.
*/
#include <cstdlib>
#include <cstring>
#include <string.h>
#include <exception>
#include <libintl.h>
#include <cxxabi.h>
#include <sys/mman.h>
#include <unistd.h>
#include <link.h>
#include <stdio.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <sys/uio.h>
#include <wchar.h>
#include <locale.h>
#include <libintl.h>
#include <ctype.h>
#include <wctype.h>
#include <langinfo.h>
#include <stdarg.h>
#include <xlocale.h>
#include <cassert>
#include "mmu.hh"
#include "libc/libc.hh"
#include <map>
#include <boost/range/adaptor/reversed.hpp>
#include <osv/stubbing.hh>
#include <api/sys/resource.h>
#include <api/math.h>
extern "C" {
void __cxa_pure_virtual(void);
void abort(void);
void __stack_chk_fail(void);
__locale_t __newlocale(int __category_mask, __const char *__locale,
__locale_t __base) __THROW;
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
{
abort("Aborted\n");
}
void abort(const char *msg)
// __cxa_atexit and __cxa_finalize:
// Gcc implements static constructors and destructors in shared-objects (DSO)
// in the following way: Static constructors are added to a list DT_INIT_ARRAY
// in the object, and we run these functions after loading the object. Gcc's
// code for each constructor calls a function __cxxabiv1::__cxa_atexit (which
// we need to implement here) to register a destructor, linked to this DSO.
// Gcc also adds a single finalization function to DT_FINI_ARRAY (which we
// call when unloading the DSO), which calls __cxxabiv1::__cxa_finalize
// (which we need to implement here) - this function is supposed to call all
// the destructors previously registered for the given DSO.
//
// This implementation is greatly simplified by the assumption that the kernel
// never exits, so our code doesn't need to work during early initialization,
// nor does __cxa_finalize(0) need to work.
typedef void (*destructor_t)(void *);
static std::map<void *, std::vector<std::pair<destructor_t,void*>>> destructors;
int __cxa_atexit(destructor_t destructor, void *arg, void *dso)
{
// As explained above, don't remember the kernel's own destructors.
if (dso == &__dso_handle)
return 0;
destructors[dso].push_back(std::make_pair(destructor, arg));
return 0;
}
int __cxa_finalize(void *dso)
{
if (!dso || dso == &__dso_handle) {
debug("__cxa_finalize() running kernel's destructors not supported\n");
return 0;
for (auto d : boost::adaptors::reverse(destructors[dso])) {
d.first(d.second);
destructors.erase(dso);
return 0;
}
}
int getpagesize()
{
return 4096;
}
int vfork()
{
debug("vfork stubbed\n");
return -1;
}
int fork()
{
debug("fork stubbed\n");
return -1;
}
NO_SYS(int execvp(const char *, char *const []));
NO_SYS(int symlink(const char *, const char *));
int mlockall(int flags)
{
return 0;
}
int munlockall(void)
{
return 0;
}
int posix_fadvise(int fd, off_t offset, off_t len, int advice)
{
switch (advice) {
case POSIX_FADV_NORMAL:
case POSIX_FADV_SEQUENTIAL:
case POSIX_FADV_RANDOM:
case POSIX_FADV_NOREUSE:
case POSIX_FADV_WILLNEED:
case POSIX_FADV_DONTNEED:
return 0;
default:
// WCTDEF(alnum), WCTDEF(alpha), WCTDEF(blank), WCTDEF(cntrl),
// WCTDEF(digit), WCTDEF(graph), WCTDEF(lower), WCTDEF(print),
// WCTDEF(punct), WCTDEF(space), WCTDEF(upper), WCTDEF(xdigit),
static unsigned short c_locale_array[384] = {
#include "ctype-data.h"
};
static struct __locale_struct c_locale = {
{ }, // __locales_data
c_locale_array + 128, // __ctype_b
};
namespace {
bool all_categories(int category_mask)
{
return (category_mask | (1 << LC_ALL)) == (1 << __LC_LAST) - 1;
}
}
struct __locale_data {
const void *values[0];
#define _NL_ITEM(category, index) (((category) << 16) | (index))
#define _NL_ITEM_CATEGORY(item) ((int) (item) >> 16)
#define _NL_ITEM_INDEX(item) ((int) (item) & 0xffff)
#define _NL_CTYPE_CLASS 0
#define _NL_CTYPE_TOUPPER 1
#define _NL_CTYPE_TOLOWER 3
__locale_t __newlocale(int category_mask, const char *locale, locale_t base)
__THROW
{
if (category_mask == 1 << LC_ALL) {
category_mask = ((1 << __LC_LAST) - 1) & ~(1 << LC_ALL);
}
assert(locale);
if (base == &c_locale) {
base = NULL;
}
if ((base == NULL || all_categories(category_mask))
&& (category_mask == 0 || strcmp(locale, "C") == 0)) {
return &c_locale;
}
struct __locale_struct result = base ? *base : c_locale;
if (category_mask == 0) {
auto result_ptr = new __locale_struct;
*result_ptr = result;
auto ctypes = result_ptr->__locales[LC_CTYPE]->values;
result_ptr->__ctype_b = (const unsigned short *)
ctypes[_NL_ITEM_INDEX(_NL_CTYPE_CLASS)] + 128;
result_ptr->__ctype_tolower = (const int *)
ctypes[_NL_ITEM_INDEX(_NL_CTYPE_TOLOWER)] + 128;
result_ptr->__ctype_toupper = (const int *)
ctypes[_NL_ITEM_INDEX(_NL_CTYPE_TOUPPER)] + 128;
return result_ptr;
}
abort();
}
long sysconf(int name)
{
switch (name) {
case _SC_CLK_TCK: return CLOCKS_PER_SEC;
case _SC_PAGESIZE: return mmu::page_size;
case _SC_NPROCESSORS_ONLN: return sched::cpus.size();
case _SC_NPROCESSORS_CONF: return sched::cpus.size();
case _SC_PHYS_PAGES: return memory::phys_mem_size / memory::page_size;
case _SC_IOV_MAX: return KERN_IOV_MAX;
case _SC_THREAD_SAFE_FUNCTIONS: return 1;
default:
debug(fmt("sysconf(): stubbed for parameter %1%\n") % name);
errno = EINVAL;
return -1;
long pathconf(const char *, int)
{
WARN_STUBBED();
return -1;
}
size_t confstr(int name, char* buf, size_t len)
{
char tmp[1];
if (!buf) {
buf = tmp;
len = 1;
}
auto set = [=] (const char* v) { return snprintf(buf, len, "%s", v); };
switch (name) {
case _CS_GNU_LIBC_VERSION: return set("glibc 2.16");
case _CS_GNU_LIBPTHREAD_VERSION: return set("NPTL 2.16");
}
debug(fmt("confstr: unknown parameter %1%\n") % name);
int mallopt(int param, int value)
{
return 0;
}
FILE *popen(const char *command, const char *type)
{
return NULL;
}
int pclose(FILE *stream)
{
return 0;
}
debug(fmt("program exited with status %d\n") % status);
int atexit(void (*func)())
{
// nothing to do
return 0;
}
int get_nprocs()
{
return sysconf(_SC_NPROCESSORS_ONLN);
}
clock_t times(struct tms *buffer)
{
debug("times not implemented\n");
return 0;
}
static int prio_find_thread(sched::thread **th, int which, int id)
{
errno = 0;
if ((which == PRIO_USER) || (which == PRIO_PGRP)) {
return 0;
}
if (which != PRIO_PROCESS) {
errno = EINVAL;
return -1;
}
if (id == 0) {
*th = sched::thread::current();
} else {
*th = sched::thread::find_by_id(id);
}
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
errno = ESRCH;
return -1;
}
return 0;
}
// Our priority formula is osv_prio = e^(prio * k), where k is a constant.
// We (arbitrarily) want osv_prio(20) = 5, and osv_prio(-20) = 1/5.
//
// So e^(20 * prio_k) = 5
// 20 * prio_k = ln(5)
// prio_k = ln(5) / 20
//
// When we are given OSv prio, obviously, the inverse formula applies:
//
// e^(prio * prio_k) = osv_prio
// prio * prio_k = ln(osv_prio)
// prio = ln(osv_prio) / prio_k
//
static constexpr float prio_k = log(5) / 20;
int getpriority(int which, int id)
{
sched::thread *th;
int ret = prio_find_thread(&th, which, id);
if (ret < 0) {
return ret;
}
// Case for which which is not a process and we should just return and not
// do anything
if (!th && !ret) {
return 0;
}
// We're not super concerned with speed during get/set priority, which we
// expect to be fairly rare. So we can use the stdlib math functions
// instead of any fast approximations.
int prio = logf(th->priority()) / prio_k;
if (prio < -20) {
prio = -20;
}
if (prio > 19) {
prio = 19;
}
return prio;
}
int setpriority(int which, int id, int prio)
{
sched::thread *th;
int ret = prio_find_thread(&th, which, id);
if (ret < 0) {
return ret;
}
if (!th && !ret) {
return 0;
}
float p = expf(prio_k * prio);
th->set_priority(p);
return 0;
}