add rudimentary data copy. Breaks often.

This commit is contained in:
2025-04-26 23:23:58 +03:00
parent cf398c55ff
commit b0d3c57615
8 changed files with 139 additions and 55 deletions

View File

@@ -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 ARRLEN(A) (sizeof((A)) / (sizeof((A)[0])))
#define HIOK(res) ((res) == HILOAD_OK) #define HIOK(res) ((res) == HILOAD_OK)
#define UNUSED(v) (void)(v)
#endif // COMMON_H_ #endif // COMMON_H_

View File

@@ -31,6 +31,7 @@ static HiloadContext context = {0};
// Callback function for dl_iterate_phdr // Callback function for dl_iterate_phdr
static int gather_module_data_callback(struct dl_phdr_info *info, size_t size, static int gather_module_data_callback(struct dl_phdr_info *info, size_t size,
void *data) { void *data) {
(void)size;
// '' for executable, fname for rest // '' for executable, fname for rest
const char *modname = info->dlpi_name; 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.dlhandle = handle;
module.address = info->dlpi_addr; module.address = info->dlpi_addr;
log_debugv(" header size: %u\n", size); // Mark executable
log_debugv(" dlpi_addr: %p\n", info->dlpi_addr); if (strcmp(modname, "") == 0) {
log_debugv(" dlpi_tls_modid: %zu\n", info->dlpi_tls_modid); module.info = hi_modinfo_add(module.info, HI_MODULE_STATE_EXEC);
log_debugv(" dlpi_tls_data: %p\n", info->dlpi_tls_data); }
Dl_info dl_info = {0}; Dl_info dl_info = {0};
if (dladdr((void *)info->dlpi_addr, &dl_info)) { 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; const char *modpath = dl_info.dli_fname;
module.name = strdup(modpath); 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_fname: %s\n", dl_info.dli_fname);
log_debugv("dli_sname: %s\n", dl_info.dli_sname); 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++) { for (size_t i = 0; i < sc_array_size(modules); i++) {
free((char *)sc_array_at(modules, i).name); free((char *)sc_array_at(modules, i).name);
} }
free(modules);
} }
HiloadResult gather_module_infos(HiModuleArray *modules) { HiloadResult gather_module_infos(HiModuleArray *modules) {
@@ -144,7 +151,7 @@ static void handle_events(struct hiFileWatcher *fw, HiModuleArray *modules) {
if (!module) { if (!module) {
log_warn("Watched module: %s not found.\n", event.pathname); log_warn("Watched module: %s not found.\n", event.pathname);
} else { } 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); 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); HiModuleData *module = &sc_array_at(&context->modules, i);
// Operate on dirty modules only // 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 // Operate only if the module is marked as operatable
const char *module_name = const char *module_name =
@@ -177,11 +184,11 @@ static HiloadResult reload_dirty_modules(HiloadContext *context) {
if (module_name) { if (module_name) {
log_info("Reloading %s...\n", 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; ret = HILOAD_FAIL;
log_error("Failed loading: %s\n", module->name); log_error("Failed loading: %s\n", module->name);
} }
log_info("Reloaded\n");
} }
} }
} }

View File

@@ -1,6 +1,7 @@
#include "moduler.h" #include "moduler.h"
#include "common.h" #include "common.h"
#include "files.h"
#include "logger/logger.h" #include "logger/logger.h"
#include "memory.h" #include "memory.h"
#include "moduler/elf.h" #include "moduler/elf.h"
@@ -19,9 +20,6 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <unistd.h> #include <unistd.h>
const char *mod_exclude_filter[] = {
"/usr/", "/lib/", "/lib64/", "/bin/", "/opt/",
};
static void *adjust_if_relative(void *ptr, void *module_base) { static void *adjust_if_relative(void *ptr, void *module_base) {
uptr p = (uptr)ptr; 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, static HiloadResult gather_patchable_symbols(struct sc_array_sym *symbols,
const char *patch_name, const char *module_name,
void *module_base) { void *module_base) {
sc_array_clear(symbols); sc_array_clear(symbols);
HiloadResult ret = HILOAD_FAIL; HiloadResult ret = HILOAD_FAIL;
int patchfd = open(patch_name, O_RDONLY); int patchfd = open(module_name, O_RDONLY);
if (patchfd == -1) { 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; return HILOAD_FAIL;
} }
@@ -77,8 +75,7 @@ static HiloadResult gather_patchable_symbols(struct sc_array_sym *symbols,
continue; continue;
} }
// Look for dynamic symbol table (SHT_DYNSYM) if (shdr.sh_type == SHT_SYMTAB) {
if (shdr.sh_type == SHT_DYNSYM) {
// Get the string table for symbol names // Get the string table for symbol names
Elf_Scn *str_scn = elf_getscn(elf, shdr.sh_link); Elf_Scn *str_scn = elf_getscn(elf, shdr.sh_link);
if (!str_scn) { if (!str_scn) {
@@ -120,18 +117,30 @@ static HiloadResult gather_patchable_symbols(struct sc_array_sym *symbols,
continue; 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; const char *name = (const char *)(str_data->d_buf) + sym.st_name;
if (sym.st_name == 0 || strlen(name) == 0) { if (sym.st_name == 0 || strlen(name) == 0) {
continue; // Skip unnamed symbols continue; // Skip unnamed symbols
} }
// Calculate actual address (base + offset)
void *sym_addr = (void *)((uintptr_t)module_base + sym.st_value); void *sym_addr = (void *)((uintptr_t)module_base + sym.st_value);
HiSymbolBind binding = symbol_bind_from_efi(GELF_ST_BIND(sym.st_info)); HiSymbolBind binding = symbol_bind_from_efi(GELF_ST_BIND(sym.st_info));
HiSymbolType type = symbol_type_from_efi(GELF_ST_TYPE(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), HiSymbol hisym = {.name = strdup(name),
.size = size,
.binding = binding, .binding = binding,
.type = type, .type = type,
.address = sym_addr}; .address = sym_addr};
@@ -151,7 +160,7 @@ cleanup:
return ret; return ret;
} }
static HiloadResult moduler_apply_module_patch(HiSymbols *symbols, static HiloadResult moduler_apply_module_patch(HiSymbols *psymbols, HiSymbols *msymbols,
MemoryRegionSpan module_memory) { MemoryRegionSpan module_memory) {
void *module_base = (void *)module_memory.region_start; 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); hi_elf_find_dynamic_segment(module_base, module_size, &elf);
for (ElfW(Dyn) *d = dyn_sct; d->d_tag != DT_NULL; d++) { 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) { switch (d->d_tag) {
case DT_RELASZ: case DT_RELASZ:
relasz = d->d_un.d_val; 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); 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; ElfW(Rela) *r = NULL;
for (size_t i = 0; i < rela_count; i++) { 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 // Check if this is a symbol we want to patch
for (size_t j = 0; j < num_symbols; j++) { 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) { if (strcmp(sym->name, name) == 0) {
sym->got_entry = got_entry; sym->got_entry = got_entry;
sym->orig_address = *got_entry; // Save the original function sym->orig_address = *got_entry; // Save the original function
*got_entry = sym->address; *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); got_entry, *got_entry);
found_count++; 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, HiloadResult moduler_reload(HiModuleArray *modules, HiModuleData *module,
struct sc_array_memreg *memregs) { 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 dlerror(); // clear old errors
char patch_name[512]; char patch_filename[512];
size_t written = 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) { if (written == 0) {
log_error("Failed to concat %s and %s\n", module->name, ".patch"); log_error("Failed to concat %s and %s\n", module->name, ".patch");
return HILOAD_FAIL; return HILOAD_FAIL;
} }
// Load patch hi_file_copy(module->name, patch_filename);
log_debug("Opening: %s\n", patch_name);
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) { if (!new_handle) {
log_error("Couldn't load: %s\n", dlerror()); 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; return HILOAD_FAIL;
} }
// refresh before using // refresh before using
read_memory_maps_self(memregs); 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; void *patch_base = (void *)patch_memory.region_start;
HiSymbols symbols; HiSymbols patch_symbols;
symbol_init_symbols(&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)) { 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; return HILOAD_FAIL;
} }
for (size_t i = 0; i < sc_array_size(modules); ++i) { for (size_t i = 0; i < sc_array_size(modules); ++i) {
HiModuleData mod = sc_array_at(modules, i); HiModuleData mod = sc_array_at(modules, i);
// Filter system and virtual modules if (!hi_modinfo_has(mod.info, HI_MODULE_STATE_PATCHABLE))
if (mod.name[0] != '/')
continue;
if (hi_string_starts_with(mod.name, ARRLEN(mod_exclude_filter),
mod_exclude_filter))
continue; 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); 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->info = hi_modinfo_clear(module->info, HI_MODULE_STATE_DIRTY);
symbol_term_symbols(&module_symbols);
module->state = HI_MODULE_STATE_CLEAN;
} }
symbol_term_symbols(&symbols); symbol_term_symbols(&patch_symbols);
return HILOAD_OK; return HILOAD_OK;
} }

View File

@@ -8,21 +8,42 @@
struct HiloadContext; struct HiloadContext;
enum HiModuleState { static const char *mod_exclude_filter[] = {
HI_MODULE_STATE_CLEAN = 0, "/usr/", "/lib/", "/lib64/", "/bin/", "/opt/",
HI_MODULE_STATE_DIRTY,
}; };
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 { typedef struct {
const char *name; // Filename if found const char *name; // Filename
void *dlhandle; void *dlhandle;
uptr address; uptr address;
u8 state; ModuleInfo info;
} HiModuleData; } HiModuleData;
sc_array_def(HiModuleData, module);
sc_array_def(HiModuleData, module);
typedef struct sc_array_module HiModuleArray; 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, HiloadResult moduler_reload(HiModuleArray *modules, HiModuleData *module,
struct sc_array_memreg *memregs); struct sc_array_memreg *memregs);

View File

@@ -1,6 +1,19 @@
#include "symbols.h" #include "symbols.h"
#include "array/sc_array.h"
#include <elf.h> #include <elf.h>
#include <string.h>
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) { HiSymbolBind symbol_bind_from_efi(u32 efi_bind) {
// clang-format off // clang-format off

View File

@@ -25,6 +25,7 @@ typedef struct HiSymbol {
const char *name; const char *name;
HiSymbolBind binding; HiSymbolBind binding;
HiSymbolType type; HiSymbolType type;
size_t size;
void *address; void *address;
void **got_entry; void **got_entry;
void *orig_address; void *orig_address;
@@ -49,6 +50,8 @@ static inline void symbol_term_symbols(HiSymbols *symbols) {
sc_array_term(symbols); sc_array_term(symbols);
} }
HiSymbol *symbol_find(HiSymbols *symbols, HiSymbol *symbol);
HiSymbolBind symbol_bind_from_efi(u32 efi_bind); HiSymbolBind symbol_bind_from_efi(u32 efi_bind);
HiSymbolType symbol_type_from_efi(u32 efi_type); HiSymbolType symbol_type_from_efi(u32 efi_type);
#endif // SYMBOLS_H_ #endif // SYMBOLS_H_

View File

@@ -11,7 +11,7 @@
int main(int argc, char *argv[]) { 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); hi_init(ARRLEN(reloadable_modules), reloadable_modules);
int modified = -1; int modified = -1;

View File

@@ -3,8 +3,8 @@
namespace minimal_lib { namespace minimal_lib {
int getNewValue(int x) { int getNewValue(int x) {
static int value = 1; static int value = 0;
value = value + x; value = value + 1;
return value; return value;
} }