Files
zinnia/core/ksyms.c
T

136 lines
3.8 KiB
C

#include <zinnia/alloc.h>
#include <zinnia/assert.h>
#include <zinnia/compiler.h>
#include <zinnia/elf.h>
#include <zinnia/hashmap.h>
#include <zinnia/init.h>
#include <zinnia/ksyms.h>
#include <zinnia/print.h>
#include <zinnia/utils.h>
#include <bits/elf.h>
#include <string.h>
[[__visibility("hidden")]]
extern const elf_sym_t __ld_dynsym_start[];
[[__visibility("hidden")]]
extern const elf_sym_t __ld_dynsym_end[];
[[__visibility("hidden")]]
extern const char __ld_dynstr_start[];
[[__visibility("hidden")]]
extern const char __ld_dynstr_end[];
[[__visibility("hidden")]]
extern const elf_dyn_t _DYNAMIC[];
static uintptr_t slide;
static bool slide_known;
static const char* kernel_name;
static hashmap(const char*, uintptr_t) symbols;
static uint64_t sym_hash(const void* key, size_t) {
const char* name = *(const char* const*)key;
return hashmap_hash_default(name, strlen(name));
}
static bool sym_eq(const void* a, const void* b, size_t) {
return !strcmp(*(const char* const*)a, *(const char* const*)b);
}
static size_t num_syms() {
return __ld_dynsym_end - __ld_dynsym_start;
}
static const char* dynstr(uint64_t offset) {
if (offset >= (size_t)(__ld_dynstr_end - __ld_dynstr_start))
return nullptr;
return &__ld_dynstr_start[offset];
}
static uintptr_t kernel_slide() {
if (slide_known)
return slide;
for (size_t i = 0; i < num_syms(); i++) {
const char* name = dynstr(__ld_dynsym_start[i].st_name);
if (name != nullptr && !strcmp(name, "ksym_lookup")) {
slide = (uintptr_t)ksym_lookup - __ld_dynsym_start[i].st_value;
break;
}
}
slide_known = true;
return slide;
}
void ksym_init() {
const uintptr_t base = kernel_slide();
for (const elf_dyn_t* dyn = _DYNAMIC; dyn->d_tag != DT_NULL; dyn++) {
if (dyn->d_tag == DT_SONAME)
kernel_name = dynstr(dyn->d_un.d_val);
}
hashmap_init(&symbols);
size_t count = 0;
for (size_t i = 0; i < num_syms(); i++) {
const elf_sym_t* sym = &__ld_dynsym_start[i];
if (sym->st_shndx == SHN_UNDEF)
continue;
const char* name = dynstr(sym->st_name);
if (name == nullptr || *name == 0)
continue;
const errno_t status = hashmap_insert(&symbols, name, sym->st_value + base, sym_hash, sym_eq);
assert(status == 0, "Failed to register kernel symbol \"%s\" (%i)", name, status);
count++;
}
log("ksyms: Registered %zu kernel symbols\n", count);
}
bool ksym_find(const char* name, uintptr_t* addr) {
uintptr_t* value = hashmap_get(&symbols, name, sym_hash, sym_eq);
if (value == nullptr)
return false;
*addr = *value;
return true;
}
const char* ksym_kernel_name() {
return kernel_name;
}
bool ksym_lookup(uintptr_t addr, const char** name, size_t* offset) {
addr -= kernel_slide();
for (size_t i = 0; i < num_syms(); i++) {
const elf_sym_t* sym = &__ld_dynsym_start[i];
if (ELF_ST_TYPE(sym->st_info) != STT_FUNC || sym->st_shndx == SHN_UNDEF)
continue;
if (addr >= sym->st_value && addr - sym->st_value < sym->st_size) {
const char* sym_name = dynstr(sym->st_name);
if (sym_name == nullptr)
return false;
*name = sym_name;
*offset = addr - sym->st_value;
return true;
}
}
return false;
}
void ksym_format(uintptr_t addr, char* buf, size_t size) {
const char* name;
size_t offset;
if (ksym_lookup(addr, &name, &offset))
ksnprintf(buf, size, "%s+0x%zx", name, offset);
else
ksnprintf(buf, size, "<\?\?\?>");
}
[[__init]]
static void ksyms_stage_init(struct boot_info*) {
ksym_init();
}
INIT_TASK(ksyms_stage, "core.ksyms", ksyms_stage_init);
INIT_DEPENDS(ksyms_stage, mem_stage);