user: implement mlibc as the libc, finally.
It's finally done.. Signed-off-by: kaguya <vpshinomiya@protonmail.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/auxv.h>
|
||||
|
||||
#include <bits/ensure.h>
|
||||
|
||||
extern "C" uintptr_t *__dlapi_entrystack();
|
||||
|
||||
int peekauxval(unsigned long type, unsigned long *out) {
|
||||
// Find the auxiliary vector by skipping args and environment.
|
||||
auto aux = __dlapi_entrystack();
|
||||
aux += *aux + 1; // Skip argc and all arguments
|
||||
__ensure(!*aux);
|
||||
aux++;
|
||||
while(*aux) // Now, we skip the environment.
|
||||
aux++;
|
||||
aux++;
|
||||
|
||||
// Parse the auxiliary vector.
|
||||
while(true) {
|
||||
auto value = aux + 1;
|
||||
if(*aux == AT_NULL) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}else if(*aux == type) {
|
||||
*out = *value;
|
||||
return 0;
|
||||
}
|
||||
aux += 2;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long getauxval(unsigned long type) {
|
||||
unsigned long value = 0;
|
||||
if(peekauxval(type, &value))
|
||||
return 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
// XXX(qookie):
|
||||
// This is here because libgcc will call into __getauxval on glibc Linux
|
||||
// (which is what it believes we are due to the aarch64-linux-gnu toolchain)
|
||||
// in order to find AT_HWCAP to discover if LSE atomics are supported.
|
||||
//
|
||||
// This is not necessary on a custom Linux toolchain and is purely an artifact of
|
||||
// using the host toolchain.
|
||||
|
||||
// __gnu_linux__ is the define checked by libgcc
|
||||
#if defined(__aarch64__) && defined(__gnu_linux__)
|
||||
|
||||
extern "C" unsigned long __getauxval(unsigned long type) {
|
||||
unsigned long value = 0;
|
||||
if(peekauxval(type, &value))
|
||||
return 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
|
||||
// for memcpy()
|
||||
#include <string.h>
|
||||
|
||||
#include <bits/ensure.h>
|
||||
#include <mlibc/allocator.hpp>
|
||||
|
||||
#include <frg/eternal.hpp>
|
||||
#include <frg/vector.hpp>
|
||||
|
||||
struct ExitHandler {
|
||||
void (*function)(void *);
|
||||
void *argument;
|
||||
void *dsoHandle;
|
||||
};
|
||||
|
||||
using ExitQueue = frg::vector<ExitHandler, MemoryAllocator>;
|
||||
|
||||
ExitQueue &getExitQueue() {
|
||||
// use frg::eternal to prevent the compiler from scheduling the destructor
|
||||
// by generating a call to __cxa_atexit().
|
||||
static frg::eternal<ExitQueue> singleton(getAllocator());
|
||||
return singleton.get();
|
||||
}
|
||||
|
||||
extern "C" int __cxa_atexit(void (*function)(void *), void *argument, void *handle) {
|
||||
ExitHandler handler;
|
||||
handler.function = function;
|
||||
handler.argument = argument;
|
||||
handler.dsoHandle = handle;
|
||||
getExitQueue().push(handler);
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" void __dlapi_exit();
|
||||
|
||||
extern "C" void __cxa_finalize(void *dso) {
|
||||
ExitQueue &eq = getExitQueue();
|
||||
for (size_t i = eq.size(); i > 0; i--) {
|
||||
auto &handler = eq[i - 1];
|
||||
if (!handler.function)
|
||||
continue;
|
||||
|
||||
if (!dso || handler.dsoHandle == dso) {
|
||||
handler.function(handler.argument);
|
||||
handler.function = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In static builds, these should be provided by the crtbegin.o/crtend.o that
|
||||
// is linked into the executable.
|
||||
#ifndef MLIBC_STATIC_BUILD
|
||||
// This is referenced by the compiler when generating constructors for global
|
||||
// C++ objects so that it can call __cxa_finalize with a unique argument.
|
||||
extern "C" { [[gnu::visibility("hidden")]] void *__dso_handle; }
|
||||
#else
|
||||
extern "C" void *__dso_handle;
|
||||
#endif
|
||||
|
||||
[[gnu::destructor]] void __mlibc_do_destructors() {
|
||||
// In normal programs this call to __cxa_finalize is provided by libgcc.
|
||||
__cxa_finalize(&__dso_handle);
|
||||
}
|
||||
|
||||
void __mlibc_do_finalize() {
|
||||
// Invoke any handlers registered with atexit (NOT associated with a DSO).
|
||||
// Note that we deliberately do not invoke other handlers here, since
|
||||
// that would destroy mlibc's global objects including stdout and flushing
|
||||
// open FILEs, but we'd like those to be available to [[gnu::destructor]]
|
||||
// functions which we invoke below.
|
||||
ExitQueue &eq = getExitQueue();
|
||||
for (size_t i = eq.size(); i > 0; i--) {
|
||||
auto &handler = eq[i - 1];
|
||||
if (!handler.function)
|
||||
continue;
|
||||
|
||||
if (!handler.dsoHandle) {
|
||||
handler.function(handler.argument);
|
||||
handler.function = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Call fini/fini_array functions of each loaded object. This is necessary
|
||||
// to implement [[gnu::destructor]]. Note that C++ applications will call
|
||||
// __cxa_finalize from here.
|
||||
__dlapi_exit();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#include <internal-config.h>
|
||||
#include <mlibc/thread.hpp>
|
||||
#include <mlibc/rtld-abi.hpp>
|
||||
|
||||
#if (defined(__riscv) || defined(__m68k__) || defined(__loongarch64)) && defined(MLIBC_STATIC_BUILD)
|
||||
// On RISC-V, m68k and loongarch64, linker optimisation is not guaranteed and so we may
|
||||
// still get calls to this function in statically linked binaries.
|
||||
// TODO: This will break dlopen calls from statically linked programs.
|
||||
extern "C" void *__tls_get_addr(struct __abi_tls_entry *entry) {
|
||||
Tcb *tcbPtr = mlibc::get_current_tcb();
|
||||
auto dtvPtr = reinterpret_cast<char *>(tcbPtr->dtvPointers[0]);
|
||||
return reinterpret_cast<void *>(dtvPtr + entry->offset + TLS_DTV_OFFSET);
|
||||
}
|
||||
#elif defined(__i386__)
|
||||
extern "C" __attribute__((regparm(1))) void *___tls_get_addr(struct __abi_tls_entry *entry) {
|
||||
return __dlapi_get_tls(entry);
|
||||
}
|
||||
#else
|
||||
extern "C" void *__tls_get_addr(struct __abi_tls_entry *entry) {
|
||||
return __dlapi_get_tls(entry);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef _SYS_AUXV_H
|
||||
#define _SYS_AUXV_H
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef __MLIBC_ABI_ONLY
|
||||
|
||||
/* mlibc extension: Like getauxval but handles errors in a sane way. */
|
||||
/* Success: Return 0. */
|
||||
/* Failure: Return -1 and set errno. */
|
||||
int peekauxval(unsigned long __type, unsigned long *__value);
|
||||
|
||||
unsigned long getauxval(unsigned long __type);
|
||||
|
||||
#endif /* !__MLIBC_ABI_ONLY */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
lsb_sources = files(
|
||||
'generic/auxv.cpp',
|
||||
'generic/dso_exit.cpp',
|
||||
'generic/tls.cpp',
|
||||
)
|
||||
|
||||
if not no_headers
|
||||
install_headers(
|
||||
'include/sys/auxv.h',
|
||||
subdir: 'sys'
|
||||
)
|
||||
endif
|
||||
|
||||
Reference in New Issue
Block a user