diff --git a/src/common.h b/src/common.h index f6282ea..9fa02f1 100644 --- a/src/common.h +++ b/src/common.h @@ -7,6 +7,7 @@ static inline u32 has_mask(u32 flags, u32 mask) { return flags & mask; } #define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define MAX(A, B) ((A) > (B) ? (A) : (B)) #define ARRLEN(A) (sizeof((A)) / (sizeof((A)[0]))) #endif // COMMON_H_ diff --git a/src/files.c b/src/files.c index a55aafe..ef870e8 100644 --- a/src/files.c +++ b/src/files.c @@ -2,7 +2,14 @@ #include "logger/logger.h" #include "string/string.h" +#include "types.h" +#include +#include + +#ifndef HI_FILE_BUFFER_SIZE +#define HI_FILE_BUFFER_SIZE 4096 +#endif char *hi_file_to_str_dyn(const char *filename) { @@ -14,3 +21,119 @@ char *hi_file_to_str_dyn(const char *filename) { 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; +} + diff --git a/src/files.h b/src/files.h index cffb46e..6064d91 100644 --- a/src/files.h +++ b/src/files.h @@ -1,6 +1,21 @@ #ifndef 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 * @@ -8,7 +23,19 @@ * with no size. The realistic optimum case contains two allocations, one for * the initial memory and a reallocation to match the string size. */ + 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_ diff --git a/src/hiload.c b/src/hiload.c index e2160b5..b8abac0 100644 --- a/src/hiload.c +++ b/src/hiload.c @@ -211,6 +211,7 @@ int hi_init(unsigned n, const char **enabled_modules) { sc_array_add(&context.enabled_modules, module_name); } + sc_array_init(&context.memory_regions); if (read_memory_maps_self(&context.memory_regions) != HILOAD_OK) { log_error("Could not populate program memory maps.\n"); return HILOAD_FAIL; diff --git a/src/memory.c b/src/memory.c index c1a4809..5995a51 100644 --- a/src/memory.c +++ b/src/memory.c @@ -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) { - sc_array_clear(regions); - sc_array_init(regions); + memory_clear_memregs(regions); char *maps_str = hi_file_to_str_dyn("/proc/self/maps"); if (!maps_str) @@ -103,10 +102,31 @@ memory_get_module_span(const struct sc_array_memreg *const regions, } } else { // we passed the module regions - if (end > 0) break; + if (end > 0) + break; } } 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); + } } diff --git a/src/memory.h b/src/memory.h index bf4a042..349fe52 100644 --- a/src/memory.h +++ b/src/memory.h @@ -27,9 +27,15 @@ typedef struct { const char *pathname; u32 permission; // enum MemoryPermissions } MemoryRegion; - 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 * before use, but uses the same buffer. */ HiloadResult read_memory_maps_self(struct sc_array_memreg *regions); diff --git a/src/moduler/elf.c b/src/moduler/elf.c index 7fa4c83..3f697bd 100644 --- a/src/moduler/elf.c +++ b/src/moduler/elf.c @@ -7,18 +7,14 @@ #include #include -void *hi_elf_load_module(const char *filename) { - int fd = open(filename, O_RDONLY); - - Elf *elf = elf_begin(fd, ELF_C_READ, NULL); +void *hi_elf_load_module(void *address, size_t n) { + Elf *elf = elf_memory(address, n); Elf_Kind modkind = elf_kind(elf); log_debug("kind: %u\n", modkind); - uptr modbase = elf_getbase(elf); - - close(fd); - return (void*)modbase; + elf_end(elf); + return address; } 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) { 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; 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) { Elf64_Dyn *dyn = (Elf64_Dyn *)segment_start; 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) { strtab = (void *)dyn->d_un.d_ptr; @@ -84,7 +80,7 @@ void hi_elf_print_module_from_memory(void *address, size_t size) { } ++dyn; } - log_debugv("\nstrtab: %p\n" + log_debug("\nstrtab: %p\n" "symtab: %p\n" "strsz: %zu\n" "syment: %zu\n" diff --git a/src/moduler/elf.h b/src/moduler/elf.h index 34e5b72..cdb8abc 100644 --- a/src/moduler/elf.h +++ b/src/moduler/elf.h @@ -1,10 +1,12 @@ #ifndef ELF_H_ #define ELF_H_ +// TODO: Move these into .c file after API has found its form +#include #include #include -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); const char *dyn_type_to_str(unsigned type); diff --git a/src/moduler/moduler.c b/src/moduler/moduler.c index 9a12008..ca944f6 100644 --- a/src/moduler/moduler.c +++ b/src/moduler/moduler.c @@ -1,21 +1,64 @@ #include "moduler.h" +#include "common.h" #include "logger/logger.h" #include "memory.h" #include "moduler/elf.h" +#include "string/string.h" #include "types.h" +#include +#include +#include +#include #include +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, - const struct sc_array_memreg *const memregs) { + struct sc_array_memreg *memregs) { MemoryRegionSpan memspan = memory_get_module_span(memregs, module->name); log_debugv("Module: %s - [%p, %p]\n", module->name, memspan.region_start, memspan.region_end); - void *address = hi_elf_load_module(module->name); - log_debug("modaddress: %p\n", address); + dlerror(); + 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; return HILOAD_OK; diff --git a/src/moduler/moduler.h b/src/moduler/moduler.h index 4a71ccd..efe39f7 100644 --- a/src/moduler/moduler.h +++ b/src/moduler/moduler.h @@ -24,7 +24,6 @@ sc_array_def(HiModuleData, module); typedef struct sc_array_module HiModuleArray; HiloadResult moduler_reload(HiModuleData *module, - const struct sc_array_memreg *const memregs); - + struct sc_array_memreg *memregs); #endif // MODULER_H_ diff --git a/src/string/string.c b/src/string/string.c index b5b82c1..7021529 100644 --- a/src/string/string.c +++ b/src/string/string.c @@ -1,10 +1,33 @@ #include "string.h" +#include "common.h" + #include #include #include #include +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) { const char *compared_filename = path; diff --git a/src/string/string.h b/src/string/string.h index f15a6b3..9ebd43e 100644 --- a/src/string/string.h +++ b/src/string/string.h @@ -3,6 +3,18 @@ #include +/** + * 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 * reading. @@ -18,8 +30,7 @@ * @param nmax if not 0, this amount of memory in bytes is read and used as * initial allocation */ -char *hi_string_from_file_dyn(const char *filename, size_t *nread, - size_t nmax); +char *hi_string_from_file_dyn(const char *filename, size_t *nread, size_t nmax); /** * Find if filename exists at the end of path.