From b0d3c576157f4b639999ab26bb881ff37eacab5e Mon Sep 17 00:00:00 2001 From: Kasper Date: Sat, 26 Apr 2025 23:23:58 +0300 Subject: [PATCH] add rudimentary data copy. Breaks often. --- src/common.h | 2 + src/hiload.c | 27 +++++---- src/moduler/moduler.c | 110 ++++++++++++++++++++++++------------ src/moduler/moduler.h | 33 +++++++++-- src/symbols.c | 13 +++++ src/symbols.h | 3 + test/manual/minimal.cpp | 2 +- test/manual/minimal_lib.cpp | 4 +- 8 files changed, 139 insertions(+), 55 deletions(-) diff --git a/src/common.h b/src/common.h index 6f799c2..f838c0c 100644 --- a/src/common.h +++ b/src/common.h @@ -11,4 +11,6 @@ static inline u32 has_mask(u32 flags, u32 mask) { return flags & mask; } #define ARRLEN(A) (sizeof((A)) / (sizeof((A)[0]))) #define HIOK(res) ((res) == HILOAD_OK) +#define UNUSED(v) (void)(v) + #endif // COMMON_H_ diff --git a/src/hiload.c b/src/hiload.c index 9ad0b22..af05801 100644 --- a/src/hiload.c +++ b/src/hiload.c @@ -31,6 +31,7 @@ static HiloadContext context = {0}; // Callback function for dl_iterate_phdr static int gather_module_data_callback(struct dl_phdr_info *info, size_t size, void *data) { + (void)size; // '' for executable, fname for rest const char *modname = info->dlpi_name; @@ -45,10 +46,10 @@ static int gather_module_data_callback(struct dl_phdr_info *info, size_t size, module.dlhandle = handle; module.address = info->dlpi_addr; - log_debugv(" header size: %u\n", size); - log_debugv(" dlpi_addr: %p\n", info->dlpi_addr); - log_debugv(" dlpi_tls_modid: %zu\n", info->dlpi_tls_modid); - log_debugv(" dlpi_tls_data: %p\n", info->dlpi_tls_data); + // Mark executable + if (strcmp(modname, "") == 0) { + module.info = hi_modinfo_add(module.info, HI_MODULE_STATE_EXEC); + } Dl_info dl_info = {0}; if (dladdr((void *)info->dlpi_addr, &dl_info)) { @@ -58,6 +59,14 @@ static int gather_module_data_callback(struct dl_phdr_info *info, size_t size, const char *modpath = dl_info.dli_fname; module.name = strdup(modpath); + // Mark everything but system and virtual modules as patchable + if (module.name[0] == '/') { + if (!hi_string_starts_with(module.name, ARRLEN(mod_exclude_filter), + mod_exclude_filter)) { + module.info = hi_modinfo_add(module.info, HI_MODULE_STATE_PATCHABLE); + } + } + log_debugv("dli_fname: %s\n", dl_info.dli_fname); log_debugv("dli_sname: %s\n", dl_info.dli_sname); @@ -97,8 +106,6 @@ static void module_infos_free(HiModuleArray *modules) { for (size_t i = 0; i < sc_array_size(modules); i++) { free((char *)sc_array_at(modules, i).name); } - - free(modules); } HiloadResult gather_module_infos(HiModuleArray *modules) { @@ -144,7 +151,7 @@ static void handle_events(struct hiFileWatcher *fw, HiModuleArray *modules) { if (!module) { log_warn("Watched module: %s not found.\n", event.pathname); } else { - module->state = HI_MODULE_STATE_DIRTY; + module->info = hi_modinfo_add(module->info, HI_MODULE_STATE_DIRTY); } event = hi_file_event_pop(context.filewatcher); } @@ -169,7 +176,7 @@ static HiloadResult reload_dirty_modules(HiloadContext *context) { HiModuleData *module = &sc_array_at(&context->modules, i); // Operate on dirty modules only - if (module->state == HI_MODULE_STATE_DIRTY) { + if (hi_modinfo_has(module->info, HI_MODULE_STATE_DIRTY)) { // Operate only if the module is marked as operatable const char *module_name = @@ -177,11 +184,11 @@ static HiloadResult reload_dirty_modules(HiloadContext *context) { if (module_name) { log_info("Reloading %s...\n", module_name); - if (!HIOK(moduler_reload(&context->modules, module, &context->memory_regions))) { + if (!HIOK(moduler_reload(&context->modules, module, + &context->memory_regions))) { ret = HILOAD_FAIL; log_error("Failed loading: %s\n", module->name); } - log_info("Reloaded\n"); } } } diff --git a/src/moduler/moduler.c b/src/moduler/moduler.c index 0413441..3528cc1 100644 --- a/src/moduler/moduler.c +++ b/src/moduler/moduler.c @@ -1,6 +1,7 @@ #include "moduler.h" #include "common.h" +#include "files.h" #include "logger/logger.h" #include "memory.h" #include "moduler/elf.h" @@ -19,9 +20,6 @@ #include #include -const char *mod_exclude_filter[] = { - "/usr/", "/lib/", "/lib64/", "/bin/", "/opt/", -}; static void *adjust_if_relative(void *ptr, void *module_base) { uptr p = (uptr)ptr; @@ -32,15 +30,15 @@ static void *adjust_if_relative(void *ptr, void *module_base) { } static HiloadResult gather_patchable_symbols(struct sc_array_sym *symbols, - const char *patch_name, + const char *module_name, void *module_base) { sc_array_clear(symbols); HiloadResult ret = HILOAD_FAIL; - int patchfd = open(patch_name, O_RDONLY); + int patchfd = open(module_name, O_RDONLY); if (patchfd == -1) { - log_error("Failed to open %s: %s\n", patch_name, strerror(errno)); + log_error("Failed to open %s: %s\n", module_name, strerror(errno)); return HILOAD_FAIL; } @@ -77,8 +75,7 @@ static HiloadResult gather_patchable_symbols(struct sc_array_sym *symbols, continue; } - // Look for dynamic symbol table (SHT_DYNSYM) - if (shdr.sh_type == SHT_DYNSYM) { + 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) { @@ -120,18 +117,30 @@ static HiloadResult gather_patchable_symbols(struct sc_array_sym *symbols, continue; } + // Assumption: Only symbols with size are defined here, and those + // without are safely ignored. Linker will handle them. + if (sym.st_size == 0) { + 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 } - // Calculate actual address (base + offset) void *sym_addr = (void *)((uintptr_t)module_base + sym.st_value); HiSymbolBind binding = symbol_bind_from_efi(GELF_ST_BIND(sym.st_info)); HiSymbolType type = symbol_type_from_efi(GELF_ST_TYPE(sym.st_info)); + size_t size = sym.st_size; - if (binding == HI_SYMBOL_BIND_GLOBAL) { + // 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)) { HiSymbol hisym = {.name = strdup(name), + .size = size, .binding = binding, .type = type, .address = sym_addr}; @@ -151,7 +160,7 @@ cleanup: return ret; } -static HiloadResult moduler_apply_module_patch(HiSymbols *symbols, +static HiloadResult moduler_apply_module_patch(HiSymbols *psymbols, HiSymbols *msymbols, MemoryRegionSpan module_memory) { void *module_base = (void *)module_memory.region_start; @@ -171,7 +180,8 @@ static HiloadResult moduler_apply_module_patch(HiSymbols *symbols, hi_elf_find_dynamic_segment(module_base, module_size, &elf); for (ElfW(Dyn) *d = dyn_sct; d->d_tag != DT_NULL; d++) { - log_debug("%s\n", hi_elf_dyntostr(d->d_tag)); + + log_debugv("%s\n", hi_elf_dyntostr(d->d_tag)); switch (d->d_tag) { case DT_RELASZ: relasz = d->d_un.d_val; @@ -217,7 +227,7 @@ static HiloadResult moduler_apply_module_patch(HiSymbols *symbols, printf("Processing %zu relocations\n", rela_count); - size_t num_symbols = sc_array_size(symbols); + size_t num_symbols = sc_array_size(psymbols); ElfW(Rela) *r = NULL; for (size_t i = 0; i < rela_count; i++) { @@ -238,17 +248,32 @@ static HiloadResult moduler_apply_module_patch(HiSymbols *symbols, // Check if this is a symbol we want to patch for (size_t j = 0; j < num_symbols; j++) { - HiSymbol *sym = &sc_array_at(symbols, j); + HiSymbol *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; - printf("Found GOT entry for '%s' at %p (points to %p)\n", name, + log_debug("Found GOT entry for '%s' at %p (points to %p)\n", name, got_entry, *got_entry); found_count++; - break; + } + } + } + + // Copy old data to new data. Breaks with layout changes. + for (size_t i = 0; i < sc_array_size(msymbols); ++i) { + HiSymbol *sym = &sc_array_at(msymbols, i); + if (sym->type == HI_SYMBOL_TYPE_OBJECT) { + HiSymbol *ps = symbol_find(psymbols, 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); } } } @@ -260,58 +285,71 @@ static HiloadResult moduler_apply_module_patch(HiSymbols *symbols, HiloadResult moduler_reload(HiModuleArray *modules, HiModuleData *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 HILOAD_OK; + } + dlerror(); // clear old errors - char patch_name[512]; + char patch_filename[512]; size_t written = - hi_strncat_buf(sizeof(patch_name), patch_name, module->name, ".patch"); + hi_strncat_buf(sizeof(patch_filename), patch_filename, module->name, ".patch"); if (written == 0) { log_error("Failed to concat %s and %s\n", module->name, ".patch"); return HILOAD_FAIL; } - // Load patch - log_debug("Opening: %s\n", patch_name); + hi_file_copy(module->name, patch_filename); - void *new_handle = dlopen(patch_name, RTLD_LAZY); + // Load patch + 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->state = HI_MODULE_STATE_CLEAN; + module->info = hi_modinfo_clear(module->info, HI_MODULE_STATE_DIRTY); return HILOAD_FAIL; } // refresh before using read_memory_maps_self(memregs); - MemoryRegionSpan patch_memory = memory_get_module_span(memregs, patch_name); + MemoryRegionSpan patch_memory = memory_get_module_span(memregs, patch_filename); void *patch_base = (void *)patch_memory.region_start; - HiSymbols symbols; - symbol_init_symbols(&symbols); + HiSymbols patch_symbols; + symbol_init_symbols(&patch_symbols); - HiloadResult ret = gather_patchable_symbols(&symbols, patch_name, patch_base); + HiloadResult ret = gather_patchable_symbols(&patch_symbols, patch_filename, patch_base); if (!HIOK(ret)) { - log_error("Failed to gather symbols for %s\n", patch_name); + log_error("Failed to gather symbols for %s\n", patch_filename); return HILOAD_FAIL; } for (size_t i = 0; i < sc_array_size(modules); ++i) { HiModuleData mod = sc_array_at(modules, i); - // Filter system and virtual modules - if (mod.name[0] != '/') - continue; - if (hi_string_starts_with(mod.name, ARRLEN(mod_exclude_filter), - mod_exclude_filter)) + if (!hi_modinfo_has(mod.info, HI_MODULE_STATE_PATCHABLE)) continue; + HiSymbols 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; + } MemoryRegionSpan module_memory = memory_get_module_span(memregs, mod.name); + moduler_apply_module_patch(&patch_symbols, &module_symbols, module_memory); - moduler_apply_module_patch(&symbols, module_memory); - - module->state = HI_MODULE_STATE_CLEAN; + module->info = hi_modinfo_clear(module->info, HI_MODULE_STATE_DIRTY); + symbol_term_symbols(&module_symbols); } - symbol_term_symbols(&symbols); + symbol_term_symbols(&patch_symbols); return HILOAD_OK; } diff --git a/src/moduler/moduler.h b/src/moduler/moduler.h index a9026d7..a74f247 100644 --- a/src/moduler/moduler.h +++ b/src/moduler/moduler.h @@ -8,21 +8,42 @@ struct HiloadContext; -enum HiModuleState { - HI_MODULE_STATE_CLEAN = 0, - HI_MODULE_STATE_DIRTY, +static const char *mod_exclude_filter[] = { + "/usr/", "/lib/", "/lib64/", "/bin/", "/opt/", }; +typedef enum { + HI_MODULE_STATE_DIRTY = (1 << 0), + HI_MODULE_STATE_PATCHABLE = (1 << 6), // non system module we will modify + HI_MODULE_STATE_EXEC = (1 << 7), // denote the current executable +} HiModuleFlags; + +typedef u8 ModuleInfo; typedef struct { - const char *name; // Filename if found + const char *name; // Filename void *dlhandle; uptr address; - u8 state; + ModuleInfo info; } HiModuleData; -sc_array_def(HiModuleData, module); + +sc_array_def(HiModuleData, module); typedef struct sc_array_module HiModuleArray; + +static inline ModuleInfo hi_modinfo_add(ModuleInfo flags, HiModuleFlags flag) { + return flags | flag; +} +static inline ModuleInfo hi_modinfo_clear(ModuleInfo flags, HiModuleFlags flag) { + return flags & ~flag; +} +static inline bool hi_modinfo_has(ModuleInfo flags, HiModuleFlags flag) { + return (flags & flag) != 0; +} + +#define HI_MODINFO_SET(info, flag) ((info) |= flag) +#define HI_MODINFO_CLEAR(info, flag) ((info) &= ~flag) + HiloadResult moduler_reload(HiModuleArray *modules, HiModuleData *module, struct sc_array_memreg *memregs); diff --git a/src/symbols.c b/src/symbols.c index 4a2a220..9d1af04 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -1,6 +1,19 @@ #include "symbols.h" +#include "array/sc_array.h" #include +#include + +HiSymbol *symbol_find(HiSymbols *symbols, HiSymbol *symbol) { + size_t namelen = strlen(symbol->name); + for (size_t i=0; i < sc_array_size(symbols); ++i) { + HiSymbol *s = &sc_array_at(symbols, i); + if (strcmp(s->name, symbol->name) == 0) { + return s; + } + } + return NULL; +} HiSymbolBind symbol_bind_from_efi(u32 efi_bind) { // clang-format off diff --git a/src/symbols.h b/src/symbols.h index 93c0484..c6cb9d7 100644 --- a/src/symbols.h +++ b/src/symbols.h @@ -25,6 +25,7 @@ typedef struct HiSymbol { const char *name; HiSymbolBind binding; HiSymbolType type; + size_t size; void *address; void **got_entry; void *orig_address; @@ -49,6 +50,8 @@ static inline void symbol_term_symbols(HiSymbols *symbols) { sc_array_term(symbols); } +HiSymbol *symbol_find(HiSymbols *symbols, HiSymbol *symbol); + HiSymbolBind symbol_bind_from_efi(u32 efi_bind); HiSymbolType symbol_type_from_efi(u32 efi_type); #endif // SYMBOLS_H_ diff --git a/test/manual/minimal.cpp b/test/manual/minimal.cpp index f2f23c0..d624bfe 100644 --- a/test/manual/minimal.cpp +++ b/test/manual/minimal.cpp @@ -11,7 +11,7 @@ int main(int argc, char *argv[]) { - const char *reloadable_modules[] = {"", "libmini.so"}; + const char *reloadable_modules[] = {"", "libmini.so", "libhiload.so"}; hi_init(ARRLEN(reloadable_modules), reloadable_modules); int modified = -1; diff --git a/test/manual/minimal_lib.cpp b/test/manual/minimal_lib.cpp index d997707..670bd34 100644 --- a/test/manual/minimal_lib.cpp +++ b/test/manual/minimal_lib.cpp @@ -3,8 +3,8 @@ namespace minimal_lib { int getNewValue(int x) { - static int value = 1; - value = value + x; + static int value = 0; + value = value + 1; return value; }