Files
zinnia/lib/freestanding/vdso.c
T
2026-08-14 21:31:01 +02:00

139 lines
3.9 KiB
C

#include <zinnia/archctl.h>
#include <zinnia/channel.h>
#include <zinnia/futex.h>
#include <zinnia/handle.h>
#include <zinnia/mem.h>
#include <zinnia/namespace.h>
#include <zinnia/random.h>
#include <zinnia/status.h>
#include <zinnia/system.h>
#include <zinnia/thread.h>
#include <common/elf.h>
#include <sys/auxv.h>
static uint8_t vdso_symbol_bind(uint8_t info) {
return ELF64_ST_BIND(info);
}
static uint8_t vdso_symbol_type(uint8_t info) {
return ELF64_ST_TYPE(info);
}
struct vdso_info {
uintptr_t base;
const char* strings;
const struct elf64_sym* symbols;
size_t symbol_count;
};
static bool vdso_info_init(struct vdso_info* info) {
const uintptr_t base = getauxval(AT_SYSINFO_EHDR);
if (!base)
return false;
const struct elf64_ehdr* ehdr = (const void*)base;
if (ehdr->e_ident[EI_MAG0] != ELF_MAG[0] || ehdr->e_ident[EI_MAG1] != ELF_MAG[1] ||
ehdr->e_ident[EI_MAG2] != ELF_MAG[2] || ehdr->e_ident[EI_MAG3] != ELF_MAG[3] ||
ehdr->e_ident[EI_CLASS] != ELFCLASS64 || ehdr->e_ident[EI_DATA] != ELFDATA2LSB ||
ehdr->e_machine != ELF_ARCH_MACHINE)
return false;
const struct elf64_dyn* dynamic = nullptr;
for (size_t i = 0; i < ehdr->e_phnum; i++) {
const struct elf64_phdr* phdr = (const void*)(base + ehdr->e_phoff + (i * ehdr->e_phentsize));
if (phdr->p_type == PT_DYNAMIC) {
dynamic = (const void*)(base + phdr->p_vaddr);
break;
}
}
if (!dynamic)
return false;
const char* strings = nullptr;
const struct elf64_sym* symbols = nullptr;
const struct elf_hash* hash = nullptr;
for (const struct elf64_dyn* entry = dynamic; entry->d_tag != DT_NULL; entry++) {
switch (entry->d_tag) {
case DT_STRTAB:
strings = (const void*)(base + entry->d_un.d_ptr);
break;
case DT_SYMTAB:
symbols = (const void*)(base + entry->d_un.d_ptr);
break;
case DT_HASH:
hash = (const void*)(base + entry->d_un.d_ptr);
break;
default:
break;
}
}
if (!strings || !symbols || !hash)
return false;
*info = (struct vdso_info){
.base = base,
.strings = strings,
.symbols = symbols,
.symbol_count = hash->n_chain,
};
return true;
}
static void* vdso_lookup(const char* name) {
struct vdso_info info;
if (!vdso_info_init(&info))
return nullptr;
for (size_t i = 0; i < info.symbol_count; i++) {
const struct elf64_sym* symbol = &info.symbols[i];
if (vdso_symbol_bind(symbol->st_info) != STB_GLOBAL || vdso_symbol_type(symbol->st_info) != STT_FUNC)
continue;
const char* symbol_name = info.strings + symbol->st_name;
const char* lhs = symbol_name;
const char* rhs = name;
while (*lhs && *lhs == *rhs) {
lhs++;
rhs++;
}
if (*lhs == 0 && *rhs == 0)
return (void*)(info.base + symbol->st_value);
}
return nullptr;
}
#define VDSO_IFUNC(name) \
static typeof(name)* resolve_##name(void) { \
return (typeof(name)*)vdso_lookup("__vdso_" #name); \
} \
typeof(name) name __attribute__((ifunc("resolve_" #name)));
VDSO_IFUNC(zn_archctl);
VDSO_IFUNC(zn_channel_create);
VDSO_IFUNC(zn_channel_write);
VDSO_IFUNC(zn_channel_read);
VDSO_IFUNC(zn_futex_wait);
VDSO_IFUNC(zn_futex_wake);
VDSO_IFUNC(zn_handle_validate);
VDSO_IFUNC(zn_handle_drop);
VDSO_IFUNC(zn_handle_clone);
VDSO_IFUNC(zn_vmo_create);
VDSO_IFUNC(zn_vmo_create_phys);
VDSO_IFUNC(zn_vmo_map);
VDSO_IFUNC(zn_vas_create);
VDSO_IFUNC(zn_ns_create);
VDSO_IFUNC(zn_ns_move);
VDSO_IFUNC(zn_random_get);
VDSO_IFUNC(zn_random_entropy);
VDSO_IFUNC(zn_status_to_string);
VDSO_IFUNC(zn_log);
VDSO_IFUNC(zn_page_size);
VDSO_IFUNC(zn_powerctl);
VDSO_IFUNC(zn_time);
VDSO_IFUNC(zn_thread_create);
VDSO_IFUNC(zn_thread_start);
VDSO_IFUNC(zn_thread_exit);