mallailua

This commit is contained in:
2025-04-21 11:29:08 +03:00
parent 69416bd25a
commit 26752683a1
12 changed files with 276 additions and 24 deletions

View File

@@ -7,6 +7,7 @@
static inline u32 has_mask(u32 flags, u32 mask) { return flags & mask; } static inline u32 has_mask(u32 flags, u32 mask) { return flags & mask; }
#define MIN(A, B) ((A) < (B) ? (A) : (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B))
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define ARRLEN(A) (sizeof((A)) / (sizeof((A)[0]))) #define ARRLEN(A) (sizeof((A)) / (sizeof((A)[0])))
#endif // COMMON_H_ #endif // COMMON_H_

View File

@@ -2,7 +2,14 @@
#include "logger/logger.h" #include "logger/logger.h"
#include "string/string.h" #include "string/string.h"
#include "types.h"
#include <errno.h>
#include <sys/stat.h>
#ifndef HI_FILE_BUFFER_SIZE
#define HI_FILE_BUFFER_SIZE 4096
#endif
char *hi_file_to_str_dyn(const char *filename) { char *hi_file_to_str_dyn(const char *filename) {
@@ -14,3 +21,119 @@ char *hi_file_to_str_dyn(const char *filename) {
return s; return s;
} }
HiFileType hi_file_interpret_as_type(const char *path) {
const char *pathname_end = path + strlen(path);
char last_char = *(pathname_end - 1);
bool as_dir = (last_char == '/') || (last_char == '\\');
return as_dir ? HI_FILE_TYPE_DIR : HI_FILE_TYPE_FILE;
}
const char *hi_file_name_from_path(const char *path) {
const char *filename = path;
// Find the last directory separator
const char *last_slash = strrchr(path, '/');
const char *last_backslash = strrchr(path, '\\');
// Determine which separator was found last (if any)
if (last_slash && (!last_backslash || last_slash > last_backslash)) {
filename = last_slash + 1;
} else if (last_backslash) {
filename = last_backslash + 1;
}
return filename;
}
HiloadResult hi_file_copy(const char *srcname, const char *destination) {
HiloadResult ret = HILOAD_FAIL;
FILE *src = fopen(srcname, "rb");
if (src == NULL) {
log_error("Couldn't open file: %s for copy\n", srcname);
return HILOAD_FAIL;
}
char buf[512];
const char *dstname = destination;
HiFileType dst_type = hi_file_interpret_as_type(destination);
if (dst_type == HI_FILE_TYPE_DIR) {
hi_strncat_buf(sizeof(buf), buf, destination,
hi_file_name_from_path(srcname));
dstname = buf;
}
FILE *dst = fopen(dstname, "wb");
if (dst == NULL) {
log_error("Failed to open destination: %s\n", dstname);
goto cleanup;
}
char buffer[HI_FILE_BUFFER_SIZE];
size_t bytes_read = 0;
while ((bytes_read = fread(buffer, 1, sizeof buffer, src)) > 0) {
if (fwrite(buffer, 1, bytes_read, dst) != bytes_read) {
log_error("Error writing to destination: %s\n", dstname);
goto cleanup;
}
}
if (ferror(src)) {
log_error("Error reading from source: %s\n");
goto cleanup;
}
ret = HILOAD_OK;
cleanup:
if (src) fclose(src);
if (dst) fclose(dst);
return ret;
}
/**
* Check file type from path.
*
* TAG: posix
*/
HiFileType hi_file_type(const char *path) {
struct stat path_stat;
int err = stat(path, &path_stat);
if (err == -1) {
if (errno == ENOENT) {
// Doesn't exist, don't count as error
return HI_FILE_TYPE_NONE;
}
log_error("Opening file failed: %s\n", strerror(errno));
return HI_FILE_TYPE_NONE;
}
if (S_ISREG(path_stat.st_mode)) {
return HI_FILE_TYPE_FILE;
}
if (S_ISDIR(path_stat.st_mode)) {
return HI_FILE_TYPE_DIR;
}
if (S_ISCHR(path_stat.st_mode)) {
return HI_FILE_TYPE_CHR;
}
if (S_ISBLK(path_stat.st_mode)) {
return HI_FILE_TYPE_BLK;
}
if (S_ISFIFO(path_stat.st_mode)) {
return HI_FILE_TYPE_FIFO;
}
if (S_ISLNK(path_stat.st_mode)) {
return HI_FILE_TYPE_LNK;
}
if (S_ISSOCK(path_stat.st_mode)) {
return HI_FILE_TYPE_SOCK;
}
return HI_FILE_TYPE_NONE;
}

View File

@@ -1,6 +1,21 @@
#ifndef FILES_H_ #ifndef FILES_H_
#define FILES_H_ #define FILES_H_
#include "types.h"
typedef enum {
HI_FILE_TYPE_FILE = 0,
HI_FILE_TYPE_DIR,
HI_FILE_TYPE_CHR,
HI_FILE_TYPE_BLK,
HI_FILE_TYPE_FIFO,
HI_FILE_TYPE_LNK,
HI_FILE_TYPE_SOCK,
HI_FILE_TYPE_COUNT,
HI_FILE_TYPE_NONE,
} HiFileType;
/** /**
* Read file dynamically to a string * Read file dynamically to a string
* *
@@ -8,7 +23,19 @@
* with no size. The realistic optimum case contains two allocations, one for * with no size. The realistic optimum case contains two allocations, one for
* the initial memory and a reallocation to match the string size. * the initial memory and a reallocation to match the string size.
*/ */
char *hi_file_to_str_dyn(const char *filename); char *hi_file_to_str_dyn(const char *filename);
/**
* Copy file \p filename to \p dest
*
* If \p dest has either '/' or '\' as the last character, it is interpreted as
* a directory and the file is copied to the directory with the same filename.
*/
HiloadResult hi_file_copy(const char *filename, const char *dest);
HiFileType hi_file_type(const char *path);
const char *hi_file_name_from_path(const char *path);
#endif // FILES_H_ #endif // FILES_H_

View File

@@ -211,6 +211,7 @@ int hi_init(unsigned n, const char **enabled_modules) {
sc_array_add(&context.enabled_modules, module_name); sc_array_add(&context.enabled_modules, module_name);
} }
sc_array_init(&context.memory_regions);
if (read_memory_maps_self(&context.memory_regions) != HILOAD_OK) { if (read_memory_maps_self(&context.memory_regions) != HILOAD_OK) {
log_error("Could not populate program memory maps.\n"); log_error("Could not populate program memory maps.\n");
return HILOAD_FAIL; return HILOAD_FAIL;

View File

@@ -31,8 +31,7 @@ HiloadResult memory_find_region(uptr ptr, struct sc_array_memreg *const regions,
} }
HiloadResult read_memory_maps_self(struct sc_array_memreg *regions) { HiloadResult read_memory_maps_self(struct sc_array_memreg *regions) {
sc_array_clear(regions); memory_clear_memregs(regions);
sc_array_init(regions);
char *maps_str = hi_file_to_str_dyn("/proc/self/maps"); char *maps_str = hi_file_to_str_dyn("/proc/self/maps");
if (!maps_str) if (!maps_str)
@@ -103,10 +102,31 @@ memory_get_module_span(const struct sc_array_memreg *const regions,
} }
} else { } else {
// we passed the module regions // we passed the module regions
if (end > 0) break; if (end > 0)
break;
} }
} }
assert(start < ~0ull && end > 0); assert(start < ~0ull && end > 0);
return (MemoryRegionSpan){.region_start = start, .region_end = end}; return (MemoryRegionSpan){.region_start = start, .region_end = end};
} }
void memory_clear_memregs(struct sc_array_memreg *regions) {
for (size_t i = 0; i < sc_array_size(regions); i++) {
memory_free_memreg(&sc_array_at(regions, i));
}
sc_array_clear(regions);
}
void memory_term_memregs(struct sc_array_memreg *regions) {
for (size_t i = 0; i < sc_array_size(regions); i++) {
memory_free_memreg(&sc_array_at(regions, i));
}
sc_array_term(regions);
}
void memory_free_memreg(MemoryRegion *reg) {
if (reg) {
free((char *)reg->pathname);
}
}

View File

@@ -27,9 +27,15 @@ typedef struct {
const char *pathname; const char *pathname;
u32 permission; // enum MemoryPermissions u32 permission; // enum MemoryPermissions
} MemoryRegion; } MemoryRegion;
sc_array_def(MemoryRegion, memreg); sc_array_def(MemoryRegion, memreg);
// Free memory and init to zero
void memory_term_memregs(struct sc_array_memreg *regions);
// Doesn't free underlying array memory, only for each region
void memory_clear_memregs(struct sc_array_memreg *regions);
// Free child memory
void memory_free_memreg(MemoryRegion *reg);
/* A pointer that can be used to place the memory regions into. Clears regions /* A pointer that can be used to place the memory regions into. Clears regions
* before use, but uses the same buffer. */ * before use, but uses the same buffer. */
HiloadResult read_memory_maps_self(struct sc_array_memreg *regions); HiloadResult read_memory_maps_self(struct sc_array_memreg *regions);

View File

@@ -7,18 +7,14 @@
#include <libelf.h> #include <libelf.h>
#include <unistd.h> #include <unistd.h>
void *hi_elf_load_module(const char *filename) { void *hi_elf_load_module(void *address, size_t n) {
int fd = open(filename, O_RDONLY);
Elf *elf = elf_begin(fd, ELF_C_READ, NULL);
Elf *elf = elf_memory(address, n);
Elf_Kind modkind = elf_kind(elf); Elf_Kind modkind = elf_kind(elf);
log_debug("kind: %u\n", modkind); log_debug("kind: %u\n", modkind);
uptr modbase = elf_getbase(elf); elf_end(elf);
return address;
close(fd);
return (void*)modbase;
} }
void hi_elf_print_module_from_memory(void *address, size_t size) { void hi_elf_print_module_from_memory(void *address, size_t size) {
@@ -36,7 +32,7 @@ void hi_elf_print_module_from_memory(void *address, size_t size) {
for (size_t i = 0; i < phdrnum; ++i) { for (size_t i = 0; i < phdrnum; ++i) {
Elf64_Phdr *p = phdr + i; Elf64_Phdr *p = phdr + i;
log_debugv("segment type: %s\n", segment_type_to_str(p->p_type)); log_debug("segment type: %s\n", segment_type_to_str(p->p_type));
size_t segment_size = p->p_memsz; size_t segment_size = p->p_memsz;
void *segment_start = address + p->p_vaddr; void *segment_start = address + p->p_vaddr;
@@ -57,7 +53,7 @@ void hi_elf_print_module_from_memory(void *address, size_t size) {
if (p->p_type == PT_DYNAMIC) { if (p->p_type == PT_DYNAMIC) {
Elf64_Dyn *dyn = (Elf64_Dyn *)segment_start; Elf64_Dyn *dyn = (Elf64_Dyn *)segment_start;
while ((void *)dyn < segment_end) { while ((void *)dyn < segment_end) {
log_debugv(" dyn type: %s\n", dyn_type_to_str(dyn->d_tag)); log_debug(" dyn type: %s\n", dyn_type_to_str(dyn->d_tag));
if (dyn->d_tag == DT_STRTAB) { if (dyn->d_tag == DT_STRTAB) {
strtab = (void *)dyn->d_un.d_ptr; strtab = (void *)dyn->d_un.d_ptr;
@@ -84,7 +80,7 @@ void hi_elf_print_module_from_memory(void *address, size_t size) {
} }
++dyn; ++dyn;
} }
log_debugv("\nstrtab: %p\n" log_debug("\nstrtab: %p\n"
"symtab: %p\n" "symtab: %p\n"
"strsz: %zu\n" "strsz: %zu\n"
"syment: %zu\n" "syment: %zu\n"

View File

@@ -1,10 +1,12 @@
#ifndef ELF_H_ #ifndef ELF_H_
#define ELF_H_ #define ELF_H_
// TODO: Move these into .c file after API has found its form
#include <elf.h>
#include <libelf.h> #include <libelf.h>
#include <gelf.h> #include <gelf.h>
void *hi_elf_load_module(const char* filename); void *hi_elf_load_module(void *address, size_t n);
void hi_elf_print_module_from_memory(void *address, size_t size); void hi_elf_print_module_from_memory(void *address, size_t size);
const char *dyn_type_to_str(unsigned type); const char *dyn_type_to_str(unsigned type);

View File

@@ -1,21 +1,64 @@
#include "moduler.h" #include "moduler.h"
#include "common.h"
#include "logger/logger.h" #include "logger/logger.h"
#include "memory.h" #include "memory.h"
#include "moduler/elf.h" #include "moduler/elf.h"
#include "string/string.h"
#include "types.h" #include "types.h"
#include <dlfcn.h>
#include <libelf.h>
#include <link.h>
#include <stdbool.h>
#include <sys/mman.h> #include <sys/mman.h>
static HiloadResult moduler_apply_patch(void *address, size_t n) {
HiloadResult ret = HILOAD_FAIL;
Elf *elf = elf_memory(address, n);
hi_elf_print_module_from_memory(address, n);
ret = HILOAD_OK;
elf_end(elf);
return ret;
}
HiloadResult moduler_reload(HiModuleData *module, HiloadResult moduler_reload(HiModuleData *module,
const struct sc_array_memreg *const memregs) { struct sc_array_memreg *memregs) {
MemoryRegionSpan memspan = memory_get_module_span(memregs, module->name); MemoryRegionSpan memspan = memory_get_module_span(memregs, module->name);
log_debugv("Module: %s - [%p, %p]\n", module->name, memspan.region_start, log_debugv("Module: %s - [%p, %p]\n", module->name, memspan.region_start,
memspan.region_end); memspan.region_end);
void *address = hi_elf_load_module(module->name); dlerror();
log_debug("modaddress: %p\n", address); char patch_name[512];
size_t written =
hi_strncat_buf(sizeof(patch_name), patch_name, module->name, ".patch");
if (!written) {
log_error("Failed to concat %s and %s\n", module->name, ".patch");
return HILOAD_FAIL;
}
log_debug("Opening: %s\n", patch_name);
void *new_handle = dlopen(patch_name, RTLD_NOW);
if (!new_handle) {
log_error("Couldn't load: %s\n", dlerror());
module->state = HI_MODULE_STATE_CLEAN;
return HILOAD_FAIL;
}
read_memory_maps_self(memregs);
memspan = memory_get_module_span(memregs, patch_name);
void *patch_address = (void *)memspan.region_start;
log_debug("patch address: %p\n", patch_address);
moduler_apply_patch(patch_address, memspan.region_end - memspan.region_start);
module->state = HI_MODULE_STATE_CLEAN; module->state = HI_MODULE_STATE_CLEAN;
return HILOAD_OK; return HILOAD_OK;

View File

@@ -24,7 +24,6 @@ sc_array_def(HiModuleData, module);
typedef struct sc_array_module HiModuleArray; typedef struct sc_array_module HiModuleArray;
HiloadResult moduler_reload(HiModuleData *module, HiloadResult moduler_reload(HiModuleData *module,
const struct sc_array_memreg *const memregs); struct sc_array_memreg *memregs);
#endif // MODULER_H_ #endif // MODULER_H_

View File

@@ -1,10 +1,33 @@
#include "string.h" #include "string.h"
#include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
size_t hi_strncat_buf(size_t buflen, char buf[buflen], const char *first, const char *second) {
if (!buf) return 0;
size_t first_len = strlen(first);
size_t second_len = strlen(second);
if (buf + first_len + second_len > buf + buflen - 1) {
return 0;
}
char * first_end = buf + first_len;
char * second_end = first_end + second_len;
strncpy(buf, first, first_end - buf);
strncpy(first_end, second, second_end - first_end);
*second_end = '\0';
return second_end - buf;
}
int hi_path_has_filename(const char *path, const char *filename) { int hi_path_has_filename(const char *path, const char *filename) {
const char *compared_filename = path; const char *compared_filename = path;

View File

@@ -3,6 +3,18 @@
#include <stddef.h> #include <stddef.h>
/**
* Concatenate two strings into a buffer.
*
* If resulting string would be longer than buflen - 1, the resulting string in \p
* buf is unchanged.
*
* @param first Null terminated character string
* @param second Null terminated character string
*/
size_t hi_strncat_buf(size_t bufsize, char buf[bufsize], const char *first,
const char *second);
/** /**
* @brief Copy file content to a null terminated string, allocating memory while * @brief Copy file content to a null terminated string, allocating memory while
* reading. * reading.
@@ -18,8 +30,7 @@
* @param nmax if not 0, this amount of memory in bytes is read and used as * @param nmax if not 0, this amount of memory in bytes is read and used as
* initial allocation * initial allocation
*/ */
char *hi_string_from_file_dyn(const char *filename, size_t *nread, char *hi_string_from_file_dyn(const char *filename, size_t *nread, size_t nmax);
size_t nmax);
/** /**
* Find if filename exists at the end of path. * Find if filename exists at the end of path.