Symbol tables, references to other mods, dependency checks

This commit is contained in:
Mark 2020-08-11 01:32:03 +03:00
parent 4ae2adb6df
commit 887ffb7e53
12 changed files with 455 additions and 126 deletions

View File

@ -10,6 +10,7 @@
#include "sys/sched.h"
#include "sys/debug.h"
#include "sys/panic.h"
#include "sys/syms.h"
#include "sys/mm.h"
#define X86_EXCEPTION_DE 0
@ -172,7 +173,7 @@ static void exc_dump(int level, struct amd64_exception_frame *frame) {
(frame->rflags & X86_FLAGS_TF) ? 'T' : '-');
uintptr_t sym_base;
const char *sym_name;
if (debug_symbol_find(frame->rip, &sym_name, &sym_base) == 0) {
if (ksym_find_location(frame->rip, &sym_name, &sym_base) == 0) {
debugf(level, " Backtrace:\n");
debugf(level, "0: %p <%s + %04x>\n", frame->rip, sym_name, frame->rip - sym_base);
debug_backtrace(DEBUG_FATAL, frame->rbp, 1, 10);

View File

@ -21,14 +21,11 @@
#include "sys/string.h"
#include "sys/debug.h"
#include "sys/panic.h"
#include "sys/syms.h"
#include "sys/attr.h"
#include "sys/elf.h"
#include "sys/mm.h"
__init(test) {
*((uint16_t *) 0xB8000) = 'A' | 0x700;
}
static uintptr_t multiboot_info_addr;
static struct multiboot_tag_mmap *multiboot_tag_mmap;
static struct multiboot_tag_module *multiboot_tag_initrd_module;
@ -102,10 +99,6 @@ void kernel_early_init(void) {
cpuid_init();
if (multiboot_tag_sections) {
debug_symbol_table_multiboot2(multiboot_tag_sections);
}
if (multiboot_tag_cmdline) {
// Set kernel command line
kinfo("Provided command line: \"%s\"\n", multiboot_tag_cmdline->string);
@ -123,6 +116,10 @@ void kernel_early_init(void) {
amd64_mm_init();
if (multiboot_tag_sections) {
ksym_set_multiboot2(multiboot_tag_sections);
}
if (rs232_avail & (1 << 0)) {
rs232_add_tty(0);
}

View File

@ -87,6 +87,8 @@ KERNEL_OBJ=$(O)/arch/amd64/entry.o \
$(O)/sys/font/logo.o \
$(O)/sys/font/default8x16.psfu.o \
$(O)/sys/mod.o \
$(O)/sys/hash.o \
$(O)/sys/syms.o \
$(O)/fs/vfs.o \
$(O)/fs/vfs_ops.o \
$(O)/fs/vfs_access.o \

View File

@ -38,12 +38,6 @@
#define kfatal(f, ...) debugf(DEBUG_FATAL, "\033[41m" DEBUG_BASE_FMT f "\033[0m", DEBUG_BASE_ARGS, ##__VA_ARGS__)
#define kprint(l, f, ...) debugf(l, DEBUG_BASE_FMT f, DEBUG_BASE_ARGS, ##__VA_ARGS__)
struct multiboot_tag_elf_sections;
void debug_symbol_table_set(uintptr_t symtab, uintptr_t strtab, size_t symtab_size, size_t strtab_size);
void debug_symbol_table_multiboot2(struct multiboot_tag_elf_sections *tag);
int debug_symbol_find(uintptr_t addr, const char **name, uintptr_t *base);
int debug_symbol_find_by_name(const char *name, uintptr_t *value);
void debug_backtrace(int level, uintptr_t rbp, int depth, int limit);
void fmtsiz(char *buf, size_t sz);

33
include/sys/hash.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include "sys/types.h"
#include "sys/list.h"
struct hash_pair {
void *key, *value;
struct list_head link;
};
#define HASH_DEBUG
struct hash {
size_t (*hash) (const void *);
struct hash_pair *(*pair_new) (void *, void *);
void (*pair_free) (struct hash_pair *);
int (*keycmp) (const void *, const void *);
#if defined(HASH_DEBUG)
void (*pair_print) (int, struct hash_pair *);
#endif
size_t bucket_count;
struct list_head *buckets;
};
int shash_init(struct hash *h, size_t cap);
int hash_insert(struct hash *h, const void *key, void *value);
struct hash_pair *hash_lookup(struct hash *h, const void *key);
#if defined(HASH_DEBUG)
void hash_dump(struct hash *h);
#else
#define hash_dump(h)
#endif

View File

@ -1,4 +1,5 @@
#pragma once
#include "sys/types.h"
int sys_module_load(const char *path, const char *params);
int sys_module_unload(const char *name);

12
include/sys/syms.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include "sys/types.h"
#include "sys/elf.h"
struct multiboot_tag_elf_sections;
void ksym_set_tables(uintptr_t symtab, uintptr_t strtab, size_t symtab_size, size_t strtab_size);
void ksym_set_multiboot2(struct multiboot_tag_elf_sections *tag);
Elf64_Sym *ksym_lookup(const char *name);
int ksym_find_location(uintptr_t addr, const char **name, uintptr_t *base);
int ksym_load(void);

View File

@ -12,3 +12,6 @@ struct module_desc {
.name = _name, \
.version = _version, \
}
#define MODULE_DEPS \
static const char __mod_deps[] __attribute__((section(".deps,\"\",@progbits //"),used))

View File

@ -6,7 +6,7 @@
#include "sys/attr.h"
#include "sys/spin.h"
#include "sys/config.h"
#include "sys/elf.h"
#include "sys/syms.h"
#include "sys/assert.h"
#include "sys/mm.h"
@ -17,111 +17,6 @@
#include "arch/amd64/hw/con.h"
#endif
////
static uintptr_t g_symtab_ptr = 0;
static uintptr_t g_strtab_ptr = 0;
static size_t g_symtab_size = 0;
static size_t g_strtab_size = 0;
// Multiboot2-loaded sections are misaligned for some reason
void debug_symbol_table_multiboot2(struct multiboot_tag_elf_sections *tag) {
kinfo("Loading kernel symbols\n");
kinfo("%u section headers:\n", tag->num);
size_t string_section_offset = tag->shndx * tag->entsize;
Elf64_Shdr *string_section_hdr = (Elf64_Shdr *) &tag->sections[string_section_offset];
const char *shstrtab = (const char *) MM_VIRTUALIZE(realigned(string_section_hdr, sh_addr));
Elf64_Shdr *symtab_shdr = NULL, *strtab_shdr = NULL;
for (size_t i = 0; i < tag->num; ++i) {
Elf64_Shdr *shdr = (Elf64_Shdr *) &tag->sections[i * tag->entsize];
const char *name = &shstrtab[realigned(shdr, sh_name)];
switch (realigned(shdr, sh_type)) {
case SHT_SYMTAB:
if (symtab_shdr) {
panic("Kernel image has 2+ symbol tables\n");
}
symtab_shdr = shdr;
break;
case SHT_STRTAB:
if (strcmp(name, ".strtab")) {
break;
}
if (strtab_shdr) {
panic("kernel image has 2+ string tables\n");
}
strtab_shdr = shdr;
break;
default:
break;
}
}
if (symtab_shdr && strtab_shdr) {
uintptr_t symtab_ptr = MM_VIRTUALIZE(realigned(symtab_shdr, sh_addr));
uintptr_t strtab_ptr = MM_VIRTUALIZE(realigned(strtab_shdr, sh_addr));
debug_symbol_table_set(symtab_ptr,
strtab_ptr,
realigned(symtab_shdr, sh_size),
realigned(strtab_shdr, sh_size));
}
}
void debug_symbol_table_set(uintptr_t s0, uintptr_t s1, size_t z0, size_t z1) {
g_symtab_ptr = s0;
g_symtab_size = z0;
g_strtab_ptr = s1;
g_strtab_size = z1;
}
int debug_symbol_find_by_name(const char *name, uintptr_t *value) {
_assert(g_symtab_ptr && g_strtab_ptr);
Elf64_Sym *symtab = (Elf64_Sym *) g_symtab_ptr;
for (size_t i = 0; i < g_symtab_size / sizeof(Elf64_Sym); ++i) {
Elf64_Sym *sym = &symtab[i];
if (sym->st_name < g_strtab_size) {
const char *r_name = (const char *) g_strtab_ptr + sym->st_name;
if (!strcmp(r_name, name)) {
*value = sym->st_value;
return 0;
}
}
}
return -1;
}
int debug_symbol_find(uintptr_t addr, const char **name, uintptr_t *base) {
if (g_symtab_ptr && g_strtab_ptr) {
size_t offset = 0;
Elf64_Sym *sym;
while (offset < g_symtab_size) {
sym = (Elf64_Sym *) (g_symtab_ptr + offset);
if (ELF_ST_TYPE(sym->st_info) == STT_FUNC) {
if (sym->st_value <= addr && sym->st_value + sym->st_size >= addr) {
*base = sym->st_value;
if (sym->st_name < g_strtab_size) {
*name = (const char *) (g_strtab_ptr + sym->st_name);
} else {
*name = "<invalid>";
}
return 0;
}
}
offset += sizeof(Elf64_Sym);
}
}
return -1;
}
void debug_backtrace(int level, uintptr_t rbp, int depth, int limit) {
// Typical layout:
// rbp + 08 == rip
@ -142,7 +37,7 @@ void debug_backtrace(int level, uintptr_t rbp, int depth, int limit) {
uintptr_t base;
const char *name;
if (debug_symbol_find(rip, &name, &base) == 0) {
if (ksym_find_location(rip, &name, &base) == 0) {
debugf(level, "%d: %p <%s + %04x>\n", depth, rip, name, rip - base);
} else {
debugf(level, "%d: %p (unknown)\n", depth, rip);

117
sys/hash.c Normal file
View File

@ -0,0 +1,117 @@
#include <stddef.h>
#include "sys/assert.h"
#include "sys/string.h"
#include "user/errno.h"
#include "sys/debug.h"
#include "sys/heap.h"
#include "sys/hash.h"
#include "sys/mem/slab.h"
#define HASH_CHECK_DUP
static struct slab_cache *shash_cache;
static size_t shash_hash(const void *_s) {
const char *s = _s;
size_t hash = 5381;
int c;
while ((c = *s++)) {
hash = ((hash << 5) + hash) + c;
}
return hash;
}
static struct hash_pair *shash_pair_new(void *key, void *value) {
// Must fit into 128-sized block
_assert(strlen(key) < 128 - sizeof(struct hash_pair));
struct hash_pair *res;
if (!shash_cache) {
shash_cache = slab_cache_get(128);
_assert(shash_cache);
}
res = slab_calloc(shash_cache);
if (!res) {
return NULL;
}
res->key = (void *) res + sizeof(struct hash_pair);
res->value = value;
strcpy((void *) res + sizeof(struct hash_pair), key);
list_head_init(&res->link);
return res;
}
static void shash_pair_free(struct hash_pair *pair) {
_assert(shash_cache);
slab_free(shash_cache, pair);
}
#if defined(HASH_DEBUG)
static void shash_pair_print(int level, struct hash_pair *pair) {
debugf(level, "%s: %p\n", pair->key, pair->value);
}
#endif
int shash_init(struct hash *h, size_t cap) {
h->hash = shash_hash;
h->pair_new = shash_pair_new;
h->pair_free = shash_pair_free;
h->keycmp = (int (*) (const void *, const void *)) strcmp;
#if defined(HASH_DEBUG)
h->pair_print = shash_pair_print;
#endif
h->bucket_count = cap;
h->buckets = kmalloc(sizeof(struct list_head) * cap);
if (!h->buckets) {
return -ENOMEM;
}
for (size_t i = 0; i < cap; ++i) {
list_head_init(&h->buckets[i]);
}
return 0;
}
int hash_insert(struct hash *h, const void *key, void *value) {
#if defined(HASH_CHECK_DUP)
_assert(!hash_lookup(h, key));
#endif
struct hash_pair *pair = h->pair_new((void *) key, value);
if (!pair) {
return -ENOMEM;
}
size_t index = h->hash(key) % h->bucket_count;
list_add(&pair->link, &h->buckets[index]);
return 0;
}
struct hash_pair *hash_lookup(struct hash *h, const void *key) {
size_t index = h->hash(key) % h->bucket_count;
struct hash_pair *pair;
list_for_each_entry(pair, &h->buckets[index], link) {
if (!h->keycmp(pair->key, key)) {
return pair;
}
}
return NULL;
}
#if defined(HASH_DEBUG)
void hash_dump(struct hash *h) {
struct hash_pair *pair;
for (size_t i = 0; i < h->bucket_count; ++i) {
list_for_each_entry(pair, &h->buckets[i], link) {
h->pair_print(DEBUG_DEFAULT, pair);
}
}
}
#endif

127
sys/mod.c
View File

@ -5,6 +5,7 @@
#include "sys/assert.h"
#include "sys/debug.h"
#include "sys/elf.h"
#include "user/module.h"
#include "sys/mem/phys.h"
#include "sys/mem/slab.h"
#include "sys/mem/vmalloc.h"
@ -12,6 +13,8 @@
#include "sys/string.h"
#include "sys/thread.h"
#include "sys/panic.h"
#include "sys/syms.h"
#include "sys/hash.h"
#include "user/errno.h"
#include "user/fcntl.h"
@ -38,6 +41,9 @@ struct object {
struct object_image image;
// Symbol values
struct hash export;
// In-memory object
uintptr_t object_base;
size_t object_page_count;
@ -45,15 +51,49 @@ struct object {
// PLT and GOT
void *gotplt;
size_t gotplt_size;
struct list_head link;
};
static struct slab_cache *g_object_cache;
static LIST_HEAD(g_module_list);
static int mod_sym_lookup(const char *name, void **value) {
struct object *mod;
struct hash_pair *pair;
list_for_each_entry(mod, &g_module_list, link) {
pair = hash_lookup(&mod->export, name);
if (pair) {
*value = pair->value;
return 0;
}
}
return -1;
}
static int mod_loaded(const char *name) {
struct object *mod;
list_for_each_entry(mod, &g_module_list, link) {
//kinfo("STRCMP %s, %s\n", mod->module_desc->name, name);
if (!strcmp(mod->module_desc->name, name)) {
return 1;
}
}
return 0;
}
static struct object *object_create(void) {
if (!g_object_cache) {
g_object_cache = slab_cache_get(sizeof(struct object));
}
return slab_calloc(g_object_cache);
struct object *r = slab_calloc(g_object_cache);
if (!r) {
return NULL;
}
_assert(shash_init(&r->export, 16) == 0);
list_head_init(&r->link);
return r;
}
static void object_free(struct object *obj) {
@ -155,6 +195,25 @@ static int object_reloc(struct object *obj) {
_assert(mm_map_single(mm_kernel, (uintptr_t) plt + i, page_phys, MM_PAGE_WRITE, 0) == 0);
}
// Export global symbols
shdr = obj->image.sh_symtab;
for (size_t i = 0; i < shdr->sh_size / shdr->sh_entsize; ++i) {
Elf64_Sym *sym = obj->image.base + shdr->sh_offset + i * shdr->sh_entsize;
Elf64_Word type = ELF64_ST_TYPE(sym->st_info);
Elf64_Word bind = ELF64_ST_BIND(sym->st_info);
Elf64_Shdr *sym_section;
if (bind == STB_GLOBAL && type != STT_SECTION) {
name = &obj->image.symstrtab[sym->st_name];
if (sym->st_shndx != SHN_UNDEF && sym->st_shndx != SHN_COMMON) {
sym_section = object_shdr(obj, sym->st_shndx);
void *value = (void *) (sym_section->sh_addr + obj->object_base + sym->st_value);
_assert(hash_insert(&obj->export, name, value) == 0);
}
}
}
// Find relocation sections
for (size_t i = 0; i < ehdr->e_shnum; ++i) {
shdr = object_shdr(obj, i);
@ -180,8 +239,19 @@ static int object_reloc(struct object *obj) {
// symbol
name = &obj->image.symstrtab[sym->st_name];
if (debug_symbol_find_by_name(name, &value) != 0) {
panic("Undefined reference to %s\n", name);
// 1. Try to lookup kernel symbol
sym = ksym_lookup(name);
if (!sym) {
// 2. Try to lookup symbol in other modules
void *r;
if (mod_sym_lookup(name, &r) != 0) {
panic("Undefined reference to %s\n", name);
}
value = (uintptr_t) r;
} else {
value = sym->st_value;
}
kdebug("Resolved %s: %p\n", name, value);
} else if (sym->st_shndx == SHN_COMMON) {
@ -334,7 +404,8 @@ static int object_extract_info(struct object *obj) {
if (type == STT_FUNC || type == STT_OBJECT || type == STT_NOTYPE) {
name = &obj->image.symstrtab[sym->st_name];
void *value = (void *) (sym->st_value + obj->object_base);
Elf64_Shdr *sym_shdr = object_shdr(obj, sym->st_shndx);
void *value = (void *) (sym->st_value + obj->object_base + sym_shdr->sh_addr);
if (!strcmp(name, SYM_MOD_ENTER)) {
obj->module_enter = value;
@ -444,6 +515,49 @@ static int object_load(struct object *obj) {
return 0;
}
static int object_check_deps(struct object *obj) {
Elf64_Ehdr *ehdr;
Elf64_Shdr *shdr;
const char *shstrtab, *name;
char depname[64];
const char *p, *e;
ehdr = obj->image.base;
shstrtab = obj->image.base + object_shdr(obj, ehdr->e_shstrndx)->sh_offset;
for (size_t i = 0; i < ehdr->e_shnum; ++i) {
shdr = object_shdr(obj, i);
name = &shstrtab[shdr->sh_name];
if (!strcmp(name, ".deps")) {
p = obj->image.base + shdr->sh_offset;
while (1) {
e = strchr(p, ',');
if (e) {
_assert((size_t) (e - p) < sizeof(depname));
strncpy(depname, p, e - p);
depname[e - p] = 0;
} else {
_assert(strlen(p) < sizeof(depname));
strcpy(depname, p);
}
if (!mod_loaded(depname)) {
kwarn("unresolved dependency: %s\n", depname);
return -ENOENT;
}
if (!e) {
break;
}
p = e + 1;
}
}
}
return 0;
}
int sys_module_unload(const char *name) {
return -ENOSYS;
}
@ -476,11 +590,16 @@ int sys_module_load(const char *_path, const char *params) {
asm volatile ("movq %0, %%cr3"::"r"(cr3):"memory");
asm volatile ("cli");
if ((res = object_check_deps(obj)) != 0) {
goto cleanup;
}
if ((res = object_load(obj)) != 0) {
goto cleanup;
}
object_finalize_load(obj);
list_add(&obj->link, &g_module_list);
debug_dump(DEBUG_DEFAULT, (void *) (obj->object_base + 0x1d), 64);

155
sys/syms.c Normal file
View File

@ -0,0 +1,155 @@
#include "sys/syms.h"
#include "sys/mm.h"
#include "sys/string.h"
#include "sys/panic.h"
#include "sys/debug.h"
#include "sys/assert.h"
#include "sys/hash.h"
#include "arch/amd64/multiboot2.h"
////
static uintptr_t g_symtab_ptr = 0;
static uintptr_t g_strtab_ptr = 0;
static size_t g_symtab_size = 0;
static size_t g_strtab_size = 0;
static struct hash g_symtab_hash;
// Multiboot2-loaded sections are misaligned for some reason
void ksym_set_multiboot2(struct multiboot_tag_elf_sections *tag) {
kinfo("Loading kernel symbols\n");
kinfo("%u section headers:\n", tag->num);
size_t string_section_offset = tag->shndx * tag->entsize;
Elf64_Shdr *string_section_hdr = (Elf64_Shdr *) &tag->sections[string_section_offset];
const char *shstrtab = (const char *) MM_VIRTUALIZE(realigned(string_section_hdr, sh_addr));
Elf64_Shdr *symtab_shdr = NULL, *strtab_shdr = NULL;
for (size_t i = 0; i < tag->num; ++i) {
Elf64_Shdr *shdr = (Elf64_Shdr *) &tag->sections[i * tag->entsize];
const char *name = &shstrtab[realigned(shdr, sh_name)];
switch (realigned(shdr, sh_type)) {
case SHT_SYMTAB:
if (symtab_shdr) {
panic("Kernel image has 2+ symbol tables\n");
}
symtab_shdr = shdr;
break;
case SHT_STRTAB:
if (strcmp(name, ".strtab")) {
break;
}
if (strtab_shdr) {
panic("kernel image has 2+ string tables\n");
}
strtab_shdr = shdr;
break;
default:
break;
}
}
if (symtab_shdr && strtab_shdr) {
uintptr_t symtab_ptr = MM_VIRTUALIZE(realigned(symtab_shdr, sh_addr));
uintptr_t strtab_ptr = MM_VIRTUALIZE(realigned(strtab_shdr, sh_addr));
ksym_set_tables(symtab_ptr,
strtab_ptr,
realigned(symtab_shdr, sh_size),
realigned(strtab_shdr, sh_size));
}
}
void ksym_set_tables(uintptr_t s0, uintptr_t s1, size_t z0, size_t z1) {
Elf64_Sym *sym;
Elf64_Word type, bind;
const char *name;
_assert(shash_init(&g_symtab_hash, 32) == 0);
// TODO: entsize?
for (size_t i = 0; i < z0 / sizeof(Elf64_Sym); ++i) {
sym = (Elf64_Sym *) (s0 + i * sizeof(Elf64_Sym));
type = ELF64_ST_TYPE(sym->st_info);
bind = ELF64_ST_BIND(sym->st_info);
if (type != STT_SECTION && bind == STB_GLOBAL) {
name = (const char *) s1 + sym->st_name;
_assert(hash_insert(&g_symtab_hash, name, sym) == 0);
}
}
// Fallback
g_symtab_ptr = s0;
g_symtab_size = z0;
g_strtab_ptr = s1;
g_strtab_size = z1;
}
static Elf64_Sym *ksym_lookup_fast(const char *name) {
struct hash_pair *p = hash_lookup(&g_symtab_hash, name);
if (p) {
return p->value;
} else {
return NULL;
}
}
Elf64_Sym *ksym_lookup(const char *name) {
Elf64_Sym *res = ksym_lookup_fast(name);
if (res) {
return res;
}
if (!g_symtab_ptr) {
kwarn("no symbol table\n");
return NULL;
}
Elf64_Sym *symtab = (Elf64_Sym *) g_symtab_ptr;
for (size_t i = 0; i < g_symtab_size / sizeof(Elf64_Sym); ++i) {
Elf64_Sym *sym = &symtab[i];
if (sym->st_name < g_strtab_size) {
const char *r_name = (const char *) g_strtab_ptr + sym->st_name;
if (!strcmp(r_name, name)) {
return sym;
}
}
}
return NULL;
}
int ksym_find_location(uintptr_t addr, const char **name, uintptr_t *base) {
if (!g_symtab_ptr) {
kwarn("no symbol table\n");
return -1;
}
if (g_symtab_ptr && g_strtab_ptr) {
size_t offset = 0;
Elf64_Sym *sym;
while (offset < g_symtab_size) {
sym = (Elf64_Sym *) (g_symtab_ptr + offset);
if (ELF_ST_TYPE(sym->st_info) == STT_FUNC) {
if (sym->st_value <= addr && sym->st_value + sym->st_size >= addr) {
*base = sym->st_value;
if (sym->st_name < g_strtab_size) {
*name = (const char *) (g_strtab_ptr + sym->st_name);
} else {
*name = "<invalid>";
}
return 0;
}
}
offset += sizeof(Elf64_Sym);
}
}
return -1;
}