unify file locations

This commit is contained in:
2025-05-03 00:34:26 +03:00
parent c6c1435f8a
commit a4af280cda
28 changed files with 73 additions and 926 deletions

394
src/moduler.c Normal file
View File

@@ -0,0 +1,394 @@
#include "moduler.h"
#include "common.h"
#include "files.h"
#include "hielf.h"
#include "histring.h"
#include "logger.h"
#include "memory.h"
#include "symbols.h"
#include "types.h"
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <link.h>
#include <stdalign.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
typedef struct {
const char *filename;
void *dlhandle;
MemoryRegionSpan memreg;
} PatchData;
static void *adjust_if_relative(void *ptr, void *module_base) {
uptr p = (uptr)ptr;
if (p && (p < (uptr)module_base)) {
p = (uptr)module_base + (uptr)ptr;
}
return (void *)p;
}
static HiResult gather_patchable_symbols(struct sc_array_sym *symbols,
const char *module_name,
void *module_base) {
sc_array_clear(symbols);
HiResult ret = HI_FAIL;
int patchfd = open(module_name, O_RDONLY);
if (patchfd == -1) {
log_error("Failed to open %s: %s\n", module_name, strerror(errno));
return HI_FAIL;
}
if (elf_version(EV_CURRENT) == EV_NONE) {
log_error("Failed to initialize libelf\n");
close(patchfd);
return HI_FAIL;
}
Elf *elf = elf_begin(patchfd, ELF_C_READ, NULL);
if (!elf) {
log_error("Failed to open elf file: %s\n", elf_errmsg(elf_errno()));
goto cleanup;
}
if (elf_kind(elf) != ELF_K_ELF) {
log_error("Not a valid ELF file\n");
goto cleanup;
}
// Get ELF header
GElf_Ehdr ehdr;
if (gelf_getehdr(elf, &ehdr) == NULL) {
log_error("Failed to get ELF header: %s\n", elf_errmsg(elf_errno()));
goto cleanup;
}
// Iterate through sections to find symbol tables
Elf_Scn *scn = NULL;
while ((scn = elf_nextscn(elf, scn)) != NULL) {
GElf_Shdr shdr;
if (gelf_getshdr(scn, &shdr) != &shdr) {
log_error("Failed to get section header: %s\n", elf_errmsg(elf_errno()));
continue;
}
if (shdr.sh_type == SHT_SYMTAB) {
// Get the string table for symbol names
Elf_Scn *str_scn = elf_getscn(elf, shdr.sh_link);
if (!str_scn) {
log_error("Failed to get string table section: %s\n",
elf_errmsg(elf_errno()));
continue;
}
GElf_Shdr str_shdr;
if (gelf_getshdr(str_scn, &str_shdr) != &str_shdr) {
log_error("Failed to get string table header: %s\n",
elf_errmsg(elf_errno()));
continue;
}
// Get string table data
Elf_Data *str_data = elf_getdata(str_scn, NULL);
if (!str_data) {
log_error("Failed to get string table data: %s\n",
elf_errmsg(elf_errno()));
continue;
}
// Get symbol table data
Elf_Data *sym_data = elf_getdata(scn, NULL);
if (!sym_data) {
log_error("Failed to get symbol table data: %s\n",
elf_errmsg(elf_errno()));
continue;
}
// Calculate number of symbols
size_t sym_count = shdr.sh_size / shdr.sh_entsize;
for (size_t i = 0; i < sym_count; ++i) {
GElf_Sym sym;
if (gelf_getsym(sym_data, i, &sym) != &sym) {
log_error("Failed to get symbol: %s\n", elf_errmsg(elf_errno()));
continue;
}
// Assumption: Only symbols with size are defined here, and those
// without are safely ignored. Linker will handle them.
// Assumption: completed.0 seems to be size 1 and appears locally, lets
// assume its useless
if (sym.st_size <= 1) {
continue;
}
const char *name = (const char *)(str_data->d_buf) + sym.st_name;
if (sym.st_name == 0 || strlen(name) == 0) {
continue; // Skip unnamed symbols
}
void *sym_addr = (void *)((uintptr_t)module_base + sym.st_value);
SymbolBind binding =
symbol_bind_from_efibind(GELF_ST_BIND(sym.st_info));
SymbolType type = symbol_type_from_efitype(GELF_ST_TYPE(sym.st_info));
size_t size = sym.st_size;
// Gather global symbols and local object symbols. Local functions are
// handled automatically, but data from the running process needs to
// be copied over
if (binding == HI_SYMBOL_BIND_GLOBAL ||
(binding == HI_SYMBOL_BIND_LOCAL &&
type == HI_SYMBOL_TYPE_OBJECT)) {
Symbol hisym = {.name = strdup(name),
.size = size,
.binding = binding,
.type = type,
.address = sym_addr};
sc_array_add(symbols, hisym);
}
}
}
}
ret = HI_OK;
cleanup:
elf_end(elf);
close(patchfd);
return ret;
}
static HiResult moduler_apply_module_patch(VectorSymbol *psymbols,
MemoryRegionSpan module_memory) {
void *module_base = (void *)module_memory.region_start;
size_t module_size = module_memory.region_end - module_memory.region_start;
void *plt = NULL;
ElfW(Rela) *jmprel = NULL;
ElfW(Rela) *rela = NULL;
ElfW(Sym) *symtab = NULL;
char *strtab = NULL;
size_t relasz = 0;
Elf *elf = NULL;
ElfW(Dyn) *dyn_sct =
hi_elf_find_dynamic_segment(module_base, module_size, &elf);
for (ElfW(Dyn) *d = dyn_sct; d->d_tag != DT_NULL; d++) {
log_debugv("%s\n", hi_elf_dyntostr(d->d_tag));
switch (d->d_tag) {
case DT_RELASZ:
relasz = d->d_un.d_val;
break;
case DT_RELA:
rela = (ElfW(Rela) *)d->d_un.d_ptr;
break;
case DT_PLTGOT:
plt = (ElfW(Rela) *)d->d_un.d_ptr;
break;
case DT_JMPREL:
jmprel = (ElfW(Rela) *)d->d_un.d_ptr;
break;
case DT_SYMTAB:
symtab = (ElfW(Sym) *)d->d_un.d_ptr;
break;
case DT_STRTAB:
strtab = (char *)d->d_un.d_ptr;
break;
}
}
// Adjust relative pointers by adding the module base address
plt = adjust_if_relative(plt, module_base);
rela = adjust_if_relative(rela, module_base);
symtab = adjust_if_relative(symtab, module_base);
strtab = adjust_if_relative(strtab, module_base);
if ((!rela && !jmprel) || !symtab || !strtab || relasz == 0) {
log_error("Missing required dynamic information\n");
elf_end(elf);
return HI_FAIL;
}
int found_count = 0;
size_t rela_count = relasz / sizeof(ElfW(Rela));
printf("Processing %zu relocations\n", rela_count);
size_t num_symbols = sc_array_size(psymbols);
ElfW(Rela) *r = NULL;
for (size_t i = 0; i < rela_count; i++) {
r = jmprel ? &jmprel[i] : &rela[i];
int sym_idx = GELF_R_SYM(r->r_info);
int rel_type = GELF_R_TYPE(r->r_info);
// We're only interested in JMP_SLOT relocations (GOT entries for functions)
if (rel_type != R_X86_64_JUMP_SLOT && rel_type != R_386_JMP_SLOT) {
continue;
}
ElfW(Sym) *sym = &symtab[sym_idx];
char *name = strtab + sym->st_name;
// Calculate the GOT entry address
void **got_entry = (void **)((char *)module_base + r->r_offset);
// Check if this is a symbol we want to patch
for (size_t j = 0; j < num_symbols; j++) {
Symbol *sym = &sc_array_at(psymbols, j);
if (strcmp(sym->name, name) == 0) {
sym->got_entry = got_entry;
sym->orig_address = *got_entry; // Save the original function
*got_entry = sym->address;
log_debug("Found GOT entry for '%s' at %p (points to %p)\n", name,
got_entry, *got_entry);
found_count++;
}
}
}
elf_end(elf);
return HI_OK;
}
PatchData moduler_create_patch(ModuleData *module) {
time_t now = time(NULL);
struct tm *t = localtime(&now);
if (t == NULL) {
return (PatchData){0};
}
char file_append[32];
if (strftime(file_append, sizeof(file_append), ".%Y%m%d%H%M%S.patch", t) ==
0) {
log_error("Failed to create patch filename.\n");
return (PatchData){0};
}
char filename[512];
size_t written =
hi_str_concat_buf(sizeof(filename), filename, module->name, file_append);
if (written == 0) {
log_error("Failed to concat %s and %s\n", module->name, ".patch");
return (PatchData){0};
}
file_copy(module->name, filename);
PatchData data = {.filename = strdup(filename)};
return data;
}
HiResult moduler_reload(VectorModuleData *modules, ModuleData *module,
struct sc_array_memreg *memregs) {
// Return OK because this isn't an error case. We just don't do it yet
// because we only handle reloading a complete solib. Can't do that with
// executables.
if (hi_modinfo_has(module->info, HI_MODULE_STATE_EXEC)) {
module->info = hi_modinfo_clear(module->info, HI_MODULE_STATE_DIRTY);
return HI_OK;
}
PatchData patch = moduler_create_patch(module);
if (!patch.filename) {
log_error("Couldn't create patch for %s\n", module->name);
return HI_FAIL;
}
// Load patch
dlerror(); // clear any previous errors
log_debug("Opening: %s\n", patch.filename);
void *new_handle = dlopen(patch.filename, RTLD_LAZY);
if (!new_handle) {
log_error("Couldn't load: %s\n", dlerror());
module->info = hi_modinfo_clear(module->info, HI_MODULE_STATE_DIRTY);
return HI_FAIL;
}
patch.dlhandle = new_handle;
// refresh cache
read_memory_maps_self(memregs);
patch.memreg = memory_get_module_span(memregs, patch.filename);
void *patch_base = (void *)patch.memreg.region_start;
VectorSymbol patch_symbols;
symbol_init_symbols(&patch_symbols);
HiResult ret =
gather_patchable_symbols(&patch_symbols, patch.filename, patch_base);
if (!HIOK(ret)) {
log_error("Failed to gather symbols for %s\n", patch.filename);
return HI_FAIL;
}
for (size_t i = 0; i < sc_array_size(modules); ++i) {
ModuleData mod = sc_array_at(modules, i);
if (!hi_modinfo_has(mod.info, HI_MODULE_STATE_PATCHABLE))
continue;
log_debug("Patching: %s\n", mod.name);
MemoryRegionSpan module_memory = memory_get_module_span(memregs, mod.name);
moduler_apply_module_patch(&patch_symbols, module_memory);
if (strncmp(mod.name, patch.filename, strlen(mod.name)) == 0) {
// If patch is for the same module, also collect local object symbols for
// coping those over.
VectorSymbol module_symbols;
symbol_init_symbols(&module_symbols);
ret = gather_patchable_symbols(&module_symbols, mod.name,
(void *)mod.address);
if (!HIOK(ret)) {
log_error("Failed to gather symbols for %s\n", mod.name);
symbol_term_symbols(&module_symbols);
continue;
}
// Copy old data to new data. Breaks with layout changes.
for (size_t i = 0; i < sc_array_size(&module_symbols); ++i) {
Symbol *sym = &sc_array_at(&module_symbols, i);
if (sym->type == HI_SYMBOL_TYPE_OBJECT) {
Symbol *ps = symbol_find(&patch_symbols, sym);
if (ps) {
if (ps->size >= sym->size) {
memcpy(ps->address, sym->address, sym->size);
} else {
memcpy(ps->address, sym->address, ps->size);
}
log_debug("Copied data for symbol: %s\n", sym->name);
}
}
}
symbol_term_symbols(&module_symbols);
}
module->info = hi_modinfo_clear(module->info, HI_MODULE_STATE_DIRTY);
}
free((char *)patch.filename);
symbol_term_symbols(&patch_symbols);
dlclose(module->dlhandle);
module->dlhandle = patch.dlhandle;
return HI_OK;
}