Remove unnecessarily stored data
This commit is contained in:
@@ -1,3 +1,11 @@
|
||||
/**
|
||||
* Hiload is an Elf hotreloader that patches C and C++ code live while
|
||||
* developing, significantly tightening the traditional feedback loop.
|
||||
*
|
||||
* Currently not supported:
|
||||
* - modifying modules loaded at runtime
|
||||
*/
|
||||
|
||||
#ifndef HILOAD_H_
|
||||
#define HILOAD_H_
|
||||
|
||||
@@ -24,7 +32,7 @@ extern "C" {
|
||||
int hi_init(size_t n, const char **enabled_modules);
|
||||
|
||||
/**
|
||||
* Frees allocated memory. Call when hiload is no longer used.
|
||||
* Call when hiload is no longer needed.
|
||||
*/
|
||||
void hi_deinit(void);
|
||||
|
||||
31
src/files.c
31
src/files.c
@@ -1,7 +1,7 @@
|
||||
#include "files.h"
|
||||
|
||||
#include "logger.h"
|
||||
#include "histring.h"
|
||||
#include "logger.h"
|
||||
#include "types.h"
|
||||
|
||||
#include <errno.h>
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
char *file_to_str_dyn(const char *filename) {
|
||||
|
||||
char *s = hi_str_from_file(filename, 0, 0);
|
||||
char *s = string_from_file(filename, 0, 0);
|
||||
if (!s) {
|
||||
sc_log_error("Failed to read file: %s\n", filename);
|
||||
return 0;
|
||||
@@ -28,23 +28,6 @@ static FileType file_interpret_as_type(const char *path) {
|
||||
return as_dir ? HI_FILE_TYPE_DIR : HI_FILE_TYPE_FILE;
|
||||
}
|
||||
|
||||
const char *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;
|
||||
}
|
||||
|
||||
HiResult file_copy(const char *srcname, const char *destination) {
|
||||
|
||||
HiResult ret = HI_FAIL;
|
||||
@@ -61,8 +44,7 @@ HiResult file_copy(const char *srcname, const char *destination) {
|
||||
|
||||
FileType dst_type = file_interpret_as_type(destination);
|
||||
if (dst_type == HI_FILE_TYPE_DIR) {
|
||||
hi_str_concat_buf(sizeof(buf), buf, destination,
|
||||
file_name_from_path(srcname));
|
||||
string_concat_buf(sizeof(buf), buf, destination, strpath_filename(srcname));
|
||||
dstname = buf;
|
||||
}
|
||||
|
||||
@@ -89,8 +71,10 @@ HiResult file_copy(const char *srcname, const char *destination) {
|
||||
ret = HI_OK;
|
||||
|
||||
cleanup:
|
||||
if (src) fclose(src);
|
||||
if (dst) fclose(dst);
|
||||
if (src)
|
||||
fclose(src);
|
||||
if (dst)
|
||||
fclose(dst);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -136,4 +120,3 @@ FileType file_type(const char *path) {
|
||||
|
||||
return HI_FILE_TYPE_NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,5 +35,3 @@ char *file_to_str_dyn(const char *filename);
|
||||
HiResult file_copy(const char *filename, const char *dest);
|
||||
|
||||
FileType file_type(const char *path);
|
||||
|
||||
const char *file_name_from_path(const char *path);
|
||||
|
||||
@@ -13,7 +13,7 @@ typedef enum {
|
||||
HI_FILE_PARENT = (1 << 30),
|
||||
} FileWatchType;
|
||||
|
||||
static inline const char *hi_file_watch_type_to_str(FileWatchType type) {
|
||||
static inline const char *filewatch_type_to_str(FileWatchType type) {
|
||||
switch (type) {
|
||||
case HI_FILE_NONE:
|
||||
return "HI_FILE_NONE";
|
||||
@@ -26,11 +26,11 @@ static inline const char *hi_file_watch_type_to_str(FileWatchType type) {
|
||||
case HI_FILE_MOVE:
|
||||
return "HI_FILE_MOVE";
|
||||
default:
|
||||
return "Unknown HI_FILE_TYPE";
|
||||
|
||||
return "Unknown HI_FILE_TYPE";
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
return "Unknown HI_FILE_TYPE";
|
||||
}
|
||||
|
||||
#endif // FILEWATCH_TYPE_H_
|
||||
|
||||
@@ -141,7 +141,7 @@ static FileEvent file_event_create(const struct inotify_event *inevent,
|
||||
return NULL_EVENT;
|
||||
}
|
||||
|
||||
FileEvent file_event_pop(FileWatcher *fw) {
|
||||
FileEvent filewatcher_event_pop(FileWatcher *fw) {
|
||||
mtx_lock(&fw->mutex);
|
||||
|
||||
FileEvent e = NULL_EVENT;
|
||||
@@ -179,7 +179,7 @@ static void file_watcher_ctx_destroy(FileWatcherContext *ctx) {
|
||||
// Declare the thread func
|
||||
int file_watcher_watch(void *arg);
|
||||
|
||||
FileWatcher *file_watcher_create() {
|
||||
FileWatcher *filewatcher_create() {
|
||||
|
||||
// Allocate context
|
||||
FileWatcher *fw = malloc(sizeof(FileWatcher));
|
||||
@@ -205,7 +205,7 @@ FileWatcher *file_watcher_create() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HiResult file_watcher_add(FileWatcher *fw, const char *filename, u32 flags) {
|
||||
HiResult filewatcher_add(FileWatcher *fw, const char *filename, u32 flags) {
|
||||
if (!fw) {
|
||||
sc_log_error("Attempted to add file watcher for '%s' with null context\n",
|
||||
filename);
|
||||
@@ -264,7 +264,7 @@ void file_watcher_notify(FileWatcher *fw) {
|
||||
}
|
||||
}
|
||||
|
||||
void file_watcher_destroy(FileWatcher *fw) {
|
||||
void filewatcher_destroy(FileWatcher *fw) {
|
||||
if (!fw)
|
||||
return;
|
||||
|
||||
@@ -275,6 +275,10 @@ void file_watcher_destroy(FileWatcher *fw) {
|
||||
}
|
||||
|
||||
file_watcher_ctx_destroy(&fw->context);
|
||||
|
||||
mtx_destroy(&fw->mutex);
|
||||
cnd_destroy(&fw->cond);
|
||||
|
||||
free(fw);
|
||||
}
|
||||
|
||||
@@ -309,7 +313,7 @@ int file_watcher_watch(void *arg) {
|
||||
if (event->len) {
|
||||
sc_log_debug("Event created: queue-size: %u, %s %s\n",
|
||||
vector_size(&ctx->events), e.pathname,
|
||||
hi_file_watch_type_to_str(e.type));
|
||||
filewatch_type_to_str(e.type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
#ifndef HI_FILEWATCHER_H_
|
||||
#define HI_FILEWATCHER_H_
|
||||
|
||||
#include "filewatch_context.h"
|
||||
#include "filewatch_type.h"
|
||||
#include "types.h"
|
||||
|
||||
/**
|
||||
* File event watcher using inotify.
|
||||
*
|
||||
* Create a FileWatcher and give it files to monitor. FileWatcher creates a thread
|
||||
* for listening system notifications. It then creates events for each filechange
|
||||
* for those files under scrutiny.
|
||||
* Create a FileWatcher and give it files to monitor. FileWatcher creates a
|
||||
* thread for listening system notifications. It then creates events for each
|
||||
* filechange for those files under scrutiny.
|
||||
*
|
||||
* Paths are stored as absolute, and this doesn't handle symlinks in watched
|
||||
* files. So if you have e.g. a symlink 'my-proj' to your project folder in
|
||||
@@ -18,6 +11,12 @@
|
||||
* not interpreted as equal.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "filewatch_context.h"
|
||||
#include "filewatch_type.h"
|
||||
#include "types.h"
|
||||
|
||||
/**
|
||||
* File Event produced by FileWatcher.
|
||||
*
|
||||
@@ -47,15 +46,15 @@ typedef struct FileEvents FileEvents;
|
||||
*
|
||||
* Starts a new watcher thread immediately.
|
||||
*/
|
||||
FileWatcher *file_watcher_create(void);
|
||||
FileWatcher *filewatcher_create(void);
|
||||
|
||||
/**
|
||||
* Destroy a previously created file watcher.
|
||||
* Destroy a previously created file watcher, cleaning resources.
|
||||
*
|
||||
* Will join the thread and destroy event stack. Any FileEvent still in
|
||||
* existence will contain invalid data after this call.
|
||||
*/
|
||||
void file_watcher_destroy(FileWatcher *fw);
|
||||
void filewatcher_destroy(FileWatcher *fw);
|
||||
|
||||
/**
|
||||
* Start watching for events on a file.
|
||||
@@ -64,11 +63,9 @@ void file_watcher_destroy(FileWatcher *fw);
|
||||
* @param pathname Absolute path to the file or directory
|
||||
* @param flags The event mask for the events to watch for
|
||||
*/
|
||||
HiResult file_watcher_add(FileWatcher *fw, const char *pathname, u32 flags);
|
||||
HiResult filewatcher_add(FileWatcher *fw, const char *pathname, u32 flags);
|
||||
|
||||
/**
|
||||
* Pop an event from event stack.
|
||||
*/
|
||||
FileEvent file_event_pop(FileWatcher *ctx);
|
||||
|
||||
#endif // HI_FILEWATCHER_H_
|
||||
FileEvent filewatcher_event_pop(FileWatcher *ctx);
|
||||
|
||||
165
src/hiload.c
165
src/hiload.c
@@ -1,4 +1,4 @@
|
||||
#include "hiload/hiload.h"
|
||||
#include "hiload.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "filewatcher/filewatcher.h"
|
||||
@@ -23,13 +23,11 @@
|
||||
* assumption is that libraries from system folders won't be actively developed
|
||||
* on, nor do they contain references to the modifiable code.
|
||||
*/
|
||||
static const char *module_exclude_filter[] = {
|
||||
static const char *system_folders[] = {
|
||||
"/usr/", "/lib/", "/lib64/", "/bin/", "/opt/",
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
VectorMemoryMap memory_regions;
|
||||
VectorStr enabled_modules;
|
||||
FileWatcher *filewatcher;
|
||||
VectorModuleData modules;
|
||||
} HiloadContext;
|
||||
@@ -37,11 +35,11 @@ typedef struct {
|
||||
static HiloadContext context = {0};
|
||||
|
||||
// Callback function for dl_iterate_phdr
|
||||
static int gather_module_data_callback(struct dl_phdr_info *info, size_t size,
|
||||
static int initial_gather_callback(struct dl_phdr_info *info, size_t size,
|
||||
void *data) {
|
||||
(void)size;
|
||||
UNUSED(size);
|
||||
|
||||
// '' for executable, fname for rest
|
||||
// '' for executable, filepath for rest
|
||||
const char *modname = info->dlpi_name;
|
||||
|
||||
log_info("Found: '%s'\n", info->dlpi_name);
|
||||
@@ -56,7 +54,7 @@ static int gather_module_data_callback(struct dl_phdr_info *info, size_t size,
|
||||
|
||||
// Mark executable
|
||||
if (strcmp(modname, "") == 0) {
|
||||
module.info = hi_modinfo_add(module.info, HI_MODULE_STATE_EXEC);
|
||||
module.info = modinfo_add(module.info, HI_MODULE_STATE_EXEC);
|
||||
}
|
||||
|
||||
Dl_info dl_info = {0};
|
||||
@@ -67,35 +65,12 @@ 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_str_starts_with(module.name, ARRLEN(module_exclude_filter),
|
||||
module_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);
|
||||
|
||||
for (size_t i = 0; i < vector_size(&context.enabled_modules); i++) {
|
||||
|
||||
// If module is in the provided modules list
|
||||
if (hi_path_has_filename(modname,
|
||||
vector_at(&context.enabled_modules, i))) {
|
||||
|
||||
// Replace shortform with str pointer from module info
|
||||
vector_at(&context.enabled_modules, i) = module.name;
|
||||
|
||||
if (!HIOK(file_watcher_add(context.filewatcher, modpath,
|
||||
HI_FILE_ALL_EVENTS))) {
|
||||
log_error("Failed to set filewatcher for: '%s'\n", modpath);
|
||||
} else {
|
||||
log_info("Watching file: %s\n", modpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// I don't know when this could happen since we're passing the info straight
|
||||
// from the info struct
|
||||
module.name = strdup(modname);
|
||||
log_warn("Couldn't load dlinfo for %s: %s\n", module.name, dlerror());
|
||||
}
|
||||
@@ -106,7 +81,7 @@ static int gather_module_data_callback(struct dl_phdr_info *info, size_t size,
|
||||
return 0; // Continue iteration
|
||||
}
|
||||
|
||||
static void module_infos_free(VectorModuleData *modules) {
|
||||
static void module_data_free(VectorModuleData *modules) {
|
||||
if (!modules)
|
||||
return;
|
||||
|
||||
@@ -115,22 +90,72 @@ static void module_infos_free(VectorModuleData *modules) {
|
||||
}
|
||||
}
|
||||
|
||||
static HiResult gather_module_infos(VectorModuleData *modules) {
|
||||
static HiResult
|
||||
module_data_initial_gather(VectorModuleData *modules, size_t enabled_count,
|
||||
const char *enabled_modules[enabled_count]) {
|
||||
|
||||
vector_init(modules);
|
||||
|
||||
// Iterate over all loaded shared objects
|
||||
if (dl_iterate_phdr(gather_module_data_callback, modules) != 0) {
|
||||
if (dl_iterate_phdr(initial_gather_callback, modules) != 0) {
|
||||
// Error occurred in callback
|
||||
module_infos_free(modules);
|
||||
module_data_free(modules);
|
||||
return HI_FAIL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < vector_size(&context.modules); ++i) {
|
||||
ModuleData *module = &vector_at(&context.modules, i);
|
||||
|
||||
// Mark everything but system and virtual modules as patchable
|
||||
if (module->name[0] == '/') {
|
||||
// Virtual module(s) don't have the root at start
|
||||
if (string_starts_with_any(module->name, ARRLEN(system_folders),
|
||||
system_folders)) {
|
||||
// Exclude anything from system folders with libraries
|
||||
continue;
|
||||
} else if (string_last_token_is(module->name, "libhiload.so", '/')) {
|
||||
// Exclude ourselves, so we don't accidentally try to override
|
||||
// ourselves while running ourselves.
|
||||
continue;
|
||||
}
|
||||
module->info = modinfo_add(module->info, HI_MODULE_STATE_PATCHABLE);
|
||||
}
|
||||
|
||||
bool enable = false;
|
||||
|
||||
for (size_t i = 0; i < enabled_count; ++i) {
|
||||
const char *enabled_module = enabled_modules[i];
|
||||
if (string_is_empty(enabled_module)) {
|
||||
if (modinfo_has(module->info, HI_MODULE_STATE_EXEC)) {
|
||||
enable = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (string_last_token_is(module->name, enabled_module, '/')) {
|
||||
enable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
module->info = modinfo_add(module->info, HI_MODULE_STATE_ENABLED);
|
||||
|
||||
// Add filewatcher
|
||||
if (!HIOK(filewatcher_add(context.filewatcher, module->name,
|
||||
HI_FILE_ALL_EVENTS))) {
|
||||
log_error("Failed to set filewatcher for: '%s'\n", module->name);
|
||||
} else {
|
||||
log_info("Watching file: %s\n", module->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HI_OK;
|
||||
}
|
||||
|
||||
static ModuleData *get_module_by_path(const char *path,
|
||||
VectorModuleData *modules) {
|
||||
static ModuleData *module_get(const char *path,
|
||||
const VectorModuleData *modules) {
|
||||
|
||||
for (size_t i = 0; i < vector_size(modules); i++) {
|
||||
ModuleData *module = &vector_at(modules, i);
|
||||
@@ -139,6 +164,7 @@ static ModuleData *get_module_by_path(const char *path,
|
||||
return module;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -147,35 +173,23 @@ static ModuleData *get_module_by_path(const char *path,
|
||||
*
|
||||
* Marks a module dirty if there has been any event.
|
||||
*/
|
||||
static void handle_events(FileWatcher *fw, VectorModuleData *modules) {
|
||||
static void handle_file_events(FileWatcher *fw, VectorModuleData *modules) {
|
||||
|
||||
FileEvent event = file_event_pop(fw);
|
||||
FileEvent event = filewatcher_event_pop(fw);
|
||||
while (event.type != HI_FILE_NONE) {
|
||||
log_debug("Event pop: %s – %s\n", event.pathname,
|
||||
hi_file_watch_type_to_str(event.type));
|
||||
filewatch_type_to_str(event.type));
|
||||
|
||||
ModuleData *module = get_module_by_path(event.pathname, modules);
|
||||
ModuleData *module = module_get(event.pathname, modules);
|
||||
if (!module) {
|
||||
log_warn("Watched module: %s not found.\n", event.pathname);
|
||||
} else {
|
||||
module->info = hi_modinfo_add(module->info, HI_MODULE_STATE_DIRTY);
|
||||
module->info = modinfo_add(module->info, HI_MODULE_STATE_DIRTY);
|
||||
}
|
||||
event = file_event_pop(context.filewatcher);
|
||||
event = filewatcher_event_pop(context.filewatcher);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *find_string_from_array(const char *needle,
|
||||
const VectorStr *haystack) {
|
||||
|
||||
for (size_t i = 0; i < vector_size(haystack); i++) {
|
||||
if (strcmp(needle, vector_at(haystack, i)) == 0) {
|
||||
return vector_at(haystack, i);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static HiResult reload_dirty_modules(HiloadContext *context) {
|
||||
|
||||
HiResult ret = HI_OK;
|
||||
@@ -183,16 +197,11 @@ static HiResult reload_dirty_modules(HiloadContext *context) {
|
||||
ModuleData *module = &vector_at(&context->modules, i);
|
||||
|
||||
// Operate on dirty modules only
|
||||
if (hi_modinfo_has(module->info, HI_MODULE_STATE_DIRTY)) {
|
||||
if (modinfo_has(module->info, HI_MODULE_STATE_DIRTY)) {
|
||||
|
||||
// Operate only if the module is marked as operatable
|
||||
const char *module_name =
|
||||
find_string_from_array(module->name, &context->enabled_modules);
|
||||
if (module_name) {
|
||||
log_info("Reloading %s...\n", module_name);
|
||||
|
||||
if (!HIOK(moduler_reload(&context->modules, module,
|
||||
&context->memory_regions))) {
|
||||
if (modinfo_has(module->info, HI_MODULE_STATE_ENABLED)) {
|
||||
log_info("Reloading %s...\n", module->name);
|
||||
if (!HIOK(moduler_reload(&context->modules, module))) {
|
||||
ret = HI_FAIL;
|
||||
log_error("Failed loading: %s\n", module->name);
|
||||
}
|
||||
@@ -205,6 +214,7 @@ static HiResult reload_dirty_modules(HiloadContext *context) {
|
||||
int hi_init(size_t n, const char **enabled_modules) {
|
||||
|
||||
if (log_init() != 0) {
|
||||
// When there's no logging, only fprintf
|
||||
fprintf(stderr, "Failed to init logger.\n");
|
||||
return 1;
|
||||
}
|
||||
@@ -212,21 +222,10 @@ int hi_init(size_t n, const char **enabled_modules) {
|
||||
log_set_thread_name("Main");
|
||||
|
||||
// Start the filewatcher
|
||||
context.filewatcher = file_watcher_create();
|
||||
context.filewatcher = filewatcher_create();
|
||||
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
const char *module_name = enabled_modules[i];
|
||||
log_info("Enabling module: '%s'\n", module_name);
|
||||
vector_add(&context.enabled_modules, module_name);
|
||||
}
|
||||
|
||||
vector_init(&context.memory_regions);
|
||||
if (memmaps_from_process(&context.memory_regions) != HI_OK) {
|
||||
log_error("Could not populate program memory maps.\n");
|
||||
return HI_FAIL;
|
||||
}
|
||||
|
||||
HiResult re = gather_module_infos(&context.modules);
|
||||
HiResult re =
|
||||
module_data_initial_gather(&context.modules, n, enabled_modules);
|
||||
if (re != HI_OK) {
|
||||
log_error("Failed to gather module infos.\n");
|
||||
return 1;
|
||||
@@ -236,15 +235,15 @@ int hi_init(size_t n, const char **enabled_modules) {
|
||||
}
|
||||
|
||||
void hi_deinit() {
|
||||
module_infos_free(&context.modules);
|
||||
file_watcher_destroy(context.filewatcher);
|
||||
module_data_free(&context.modules);
|
||||
filewatcher_destroy(context.filewatcher);
|
||||
log_term();
|
||||
memset(&context, 0, sizeof(context));
|
||||
}
|
||||
|
||||
HiResult hi_reload() {
|
||||
|
||||
handle_events(context.filewatcher, &context.modules);
|
||||
handle_file_events(context.filewatcher, &context.modules);
|
||||
reload_dirty_modules(&context);
|
||||
return HI_OK;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
size_t hi_str_concat_buf(size_t buflen, char buf[buflen], const char *first,
|
||||
size_t string_concat_buf(size_t buflen, char buf[buflen], const char *first,
|
||||
const char *second) {
|
||||
if (!buf)
|
||||
return 0;
|
||||
@@ -16,6 +16,7 @@ size_t hi_str_concat_buf(size_t buflen, char buf[buflen], const char *first,
|
||||
size_t second_len = strlen(second);
|
||||
|
||||
if (buf + first_len + second_len > buf + buflen - 1) {
|
||||
// If combined length of the strings wouldn't fit in the buffer, exit early
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -30,22 +31,7 @@ size_t hi_str_concat_buf(size_t buflen, char buf[buflen], const char *first,
|
||||
return second_end - buf;
|
||||
}
|
||||
|
||||
int hi_path_has_filename(const char *path, const char *filename) {
|
||||
|
||||
const char *compared_filename = path;
|
||||
const char *basename = strrchr(path, '/');
|
||||
|
||||
if (basename)
|
||||
compared_filename = basename + 1;
|
||||
|
||||
if (strcmp(compared_filename, filename) == 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *hi_str_from_file(const char *filename, size_t *nread,
|
||||
size_t nmax) {
|
||||
char *string_from_file(const char *filename, size_t *nread, size_t nmax) {
|
||||
|
||||
FILE *f = fopen(filename, "r");
|
||||
if (!f) {
|
||||
@@ -107,17 +93,61 @@ char *hi_str_from_file(const char *filename, size_t *nread,
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char *hi_str_starts_with(const char path[static 1], size_t listn,
|
||||
const char *pathlist[listn]) {
|
||||
bool string_is_empty(const char s[static 1]) { return s[0] == '\0'; }
|
||||
|
||||
if (!pathlist)
|
||||
return path;
|
||||
size_t pathlen = strlen(path);
|
||||
bool string_starts_with(const char s[static 1],
|
||||
const char startswith[static 1]) {
|
||||
size_t startswith_len = strlen(startswith);
|
||||
size_t s_len = strlen(s);
|
||||
|
||||
if (startswith_len > s_len)
|
||||
return false;
|
||||
size_t len = MIN(strlen(s), strlen(startswith));
|
||||
bool res = strncmp(s, startswith, len) == 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
const char *string_starts_with_any(const char s[static 1], size_t listn,
|
||||
const char *slist[listn]) {
|
||||
if (!slist)
|
||||
return s;
|
||||
size_t pathlen = strlen(s);
|
||||
for (size_t i = 0; i < listn; ++i) {
|
||||
size_t len = MIN(strlen(pathlist[i]), pathlen);
|
||||
if (strncmp(path, pathlist[i], len) == 0) {
|
||||
return pathlist[i];
|
||||
size_t len = MIN(strlen(slist[i]), pathlen);
|
||||
if (strncmp(s, slist[i], len) == 0) {
|
||||
return slist[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *string_ends_with_any(const char s[static 1], size_t listn,
|
||||
const char *slist[listn]) {
|
||||
if (!slist)
|
||||
return s;
|
||||
size_t slen = strlen(s);
|
||||
for (size_t i = 0; i < listn; ++i) {
|
||||
|
||||
size_t len = MIN(strlen(slist[i]), slen);
|
||||
if (strncmp(s, slist[i], len) == 0) {
|
||||
return slist[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int string_last_token_is(const char *s, const char *ifthis, char delim) {
|
||||
|
||||
const char *last_token = s;
|
||||
const char *basename = strrchr(s, delim);
|
||||
|
||||
if (basename)
|
||||
last_token = basename + 1;
|
||||
|
||||
if (strcmp(last_token, ifthis) == 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *strpath_filename(const char *s) { return strrchr(s, '/'); }
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* Concatenate two strings into a buffer.
|
||||
*
|
||||
* If resulting string would be longer than @a buflen - 1, @a buf remains unchanged.
|
||||
* If resulting string would be longer than @a buflen - 1, @a buf remains
|
||||
* unchanged.
|
||||
*
|
||||
* @param bufsize Size of the destination buffer @a buf
|
||||
* @param buf The destination buffer
|
||||
* @param first Null terminated character string
|
||||
* @param second Null terminated character string
|
||||
*/
|
||||
size_t hi_str_concat_buf(size_t bufsize, char buf[bufsize],
|
||||
const char *first, const char *second);
|
||||
size_t string_concat_buf(size_t bufsize, char buf[bufsize], const char *first,
|
||||
const char *second);
|
||||
|
||||
/**
|
||||
* Copy file content to a null terminated string, allocating memory while
|
||||
@@ -31,20 +33,34 @@ size_t hi_str_concat_buf(size_t bufsize, char buf[bufsize],
|
||||
* @param nmax if not 0, this amount of memory in bytes is read and used as
|
||||
* initial allocation
|
||||
*/
|
||||
char *hi_str_from_file(const char *filename, size_t *nread,
|
||||
size_t nmax);
|
||||
char *string_from_file(const char *filename, size_t *nread, size_t nmax);
|
||||
|
||||
/**
|
||||
* Return the first string from @a pathlist that is fully included in @a path.
|
||||
* Test if null terminated string is empty
|
||||
*/
|
||||
const char *hi_str_starts_with(const char path[static 1], size_t listn,
|
||||
const char *pathlist[listn]);
|
||||
bool string_is_empty(const char s[static 1]);
|
||||
|
||||
/**
|
||||
* Return the first string from @a slist that @a s starts with.
|
||||
*/
|
||||
const char *string_starts_with_any(const char s[static 1], size_t listn,
|
||||
const char *slist[listn]);
|
||||
/**
|
||||
* Return the first string from @a slist that @a s ends with.
|
||||
*/
|
||||
const char *string_ends_with_any(const char s[static 1], size_t listn,
|
||||
const char *slist[listn]);
|
||||
|
||||
/**
|
||||
* Find if @a filename exists at the end of @a path.
|
||||
*
|
||||
* ```c
|
||||
*
|
||||
* if (string_last_token_is(fullpath, filename, '/')) { ... }
|
||||
* ````
|
||||
*
|
||||
* @return 0 if not found, positive if found
|
||||
*/
|
||||
int hi_path_has_filename(const char *path, const char *filename);
|
||||
int string_last_token_is(const char *s, const char *ifthis, char delim);
|
||||
|
||||
const char *strpath_filename(const char *s);
|
||||
|
||||
@@ -264,7 +264,7 @@ static HiResult moduler_apply_module_patch(VectorSymbol *psymbols,
|
||||
return HI_OK;
|
||||
}
|
||||
|
||||
PatchData moduler_create_patch(ModuleData *module) {
|
||||
static PatchData moduler_create_patch(ModuleData *module) {
|
||||
|
||||
time_t now = time(NULL);
|
||||
struct tm *t = localtime(&now);
|
||||
@@ -281,7 +281,7 @@ PatchData moduler_create_patch(ModuleData *module) {
|
||||
|
||||
char filename[512];
|
||||
size_t written =
|
||||
hi_str_concat_buf(sizeof(filename), filename, module->name, file_append);
|
||||
string_concat_buf(sizeof(filename), filename, module->name, file_append);
|
||||
|
||||
if (written == 0) {
|
||||
log_error("Failed to concat %s and %s\n", module->name, ".patch");
|
||||
@@ -294,14 +294,13 @@ PatchData moduler_create_patch(ModuleData *module) {
|
||||
return data;
|
||||
}
|
||||
|
||||
HiResult moduler_reload(VectorModuleData *modules, ModuleData *module,
|
||||
VectorMemoryMap *memmaps) {
|
||||
HiResult moduler_reload(VectorModuleData *modules, ModuleData *module) {
|
||||
|
||||
// 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);
|
||||
if (modinfo_has(module->info, HI_MODULE_STATE_EXEC)) {
|
||||
module->info = modinfo_clear(module->info, HI_MODULE_STATE_DIRTY);
|
||||
return HI_OK;
|
||||
}
|
||||
|
||||
@@ -317,18 +316,19 @@ HiResult moduler_reload(VectorModuleData *modules, ModuleData *module,
|
||||
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);
|
||||
module->info = modinfo_clear(module->info, HI_MODULE_STATE_DIRTY);
|
||||
return HI_FAIL;
|
||||
}
|
||||
patch.dlhandle = new_handle;
|
||||
|
||||
// refresh cache
|
||||
if (!HIOK(memmaps_from_process(memmaps))) {
|
||||
VectorMemoryMap memmaps;
|
||||
vector_init(&memmaps);
|
||||
if (!HIOK(memmaps_from_process(&memmaps))) {
|
||||
log_error("Failed to collect memory information for process\n");
|
||||
return HI_FAIL;
|
||||
}
|
||||
|
||||
patch.memreg = memmaps_find_by_name(patch.filename, memmaps);
|
||||
patch.memreg = memmaps_find_by_name(patch.filename, &memmaps);
|
||||
void *patch_base = (void *)patch.memreg.start;
|
||||
|
||||
VectorSymbol patch_symbols;
|
||||
@@ -344,11 +344,11 @@ HiResult moduler_reload(VectorModuleData *modules, ModuleData *module,
|
||||
for (size_t i = 0; i < vector_size(modules); ++i) {
|
||||
ModuleData mod = vector_at(modules, i);
|
||||
|
||||
if (!hi_modinfo_has(mod.info, HI_MODULE_STATE_PATCHABLE))
|
||||
if (!modinfo_has(mod.info, HI_MODULE_STATE_PATCHABLE))
|
||||
continue;
|
||||
|
||||
log_debug("Patching: %s\n", mod.name);
|
||||
MemorySpan module_memory = memmaps_find_by_name(mod.name, memmaps);
|
||||
MemorySpan module_memory = memmaps_find_by_name(mod.name, &memmaps);
|
||||
moduler_apply_module_patch(&patch_symbols, module_memory);
|
||||
|
||||
if (strncmp(mod.name, patch.filename, strlen(mod.name)) == 0) {
|
||||
@@ -383,7 +383,7 @@ HiResult moduler_reload(VectorModuleData *modules, ModuleData *module,
|
||||
symbol_term(&module_symbols);
|
||||
}
|
||||
|
||||
module->info = hi_modinfo_clear(module->info, HI_MODULE_STATE_DIRTY);
|
||||
module->info = modinfo_clear(module->info, HI_MODULE_STATE_DIRTY);
|
||||
}
|
||||
|
||||
free((char *)patch.filename);
|
||||
|
||||
@@ -8,33 +8,37 @@
|
||||
struct HiloadContext;
|
||||
|
||||
typedef enum {
|
||||
HI_MODULE_STATE_DIRTY = (1 << 0),
|
||||
HI_MODULE_STATE_ENABLED = (1 << 0),
|
||||
HI_MODULE_STATE_DIRTY = (1 << 1),
|
||||
HI_MODULE_STATE_PATCHABLE = (1 << 6), // non system module we will modify
|
||||
HI_MODULE_STATE_EXEC = (1 << 7), // denote the current executable
|
||||
} ModuleFlags;
|
||||
|
||||
typedef u8 ModuleInfo;
|
||||
typedef u32 ModuleInfo;
|
||||
typedef struct {
|
||||
const char *name; // Filename
|
||||
/// Owning. Filename for the module.
|
||||
const char *name;
|
||||
/// Handle given by dlopen
|
||||
void *dlhandle;
|
||||
/// Start address for the module
|
||||
uptr address;
|
||||
/// Additional information, see @a ModuleFlags
|
||||
ModuleInfo info;
|
||||
} ModuleData;
|
||||
|
||||
vector_def(ModuleData, ModuleData);
|
||||
|
||||
static inline ModuleInfo hi_modinfo_add(ModuleInfo flags, ModuleFlags flag) {
|
||||
static inline ModuleInfo modinfo_add(ModuleInfo flags, ModuleFlags flag) {
|
||||
return flags | flag;
|
||||
}
|
||||
static inline ModuleInfo hi_modinfo_clear(ModuleInfo flags, ModuleFlags flag) {
|
||||
static inline ModuleInfo modinfo_clear(ModuleInfo flags, ModuleFlags flag) {
|
||||
return flags & ~flag;
|
||||
}
|
||||
static inline bool hi_modinfo_has(ModuleInfo flags, ModuleFlags flag) {
|
||||
static inline bool modinfo_has(ModuleInfo flags, ModuleFlags flag) {
|
||||
return (flags & flag) != 0;
|
||||
}
|
||||
|
||||
#define HI_MODINFO_SET(info, flag) ((info) |= flag)
|
||||
#define HI_MODINFO_CLEAR(info, flag) ((info) &= ~flag)
|
||||
|
||||
HiResult moduler_reload(VectorModuleData *modules, ModuleData *module,
|
||||
VectorMemoryMap *memregs);
|
||||
HiResult moduler_reload(VectorModuleData *modules, ModuleData *module);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "minimal_lib.h"
|
||||
|
||||
#include "../../include/hiload/hiload.h"
|
||||
#include "../../include/hiload.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <chrono>
|
||||
|
||||
Reference in New Issue
Block a user