From fb763cdf641f67e0f2e089027e70b79b03222d58 Mon Sep 17 00:00:00 2001 From: Kasper Date: Sun, 16 Mar 2025 17:45:09 +0200 Subject: [PATCH] heload, a mess --- CMakeLists.txt | 9 +- cmake/clang-toolchain.cmake | 43 +++++ heload/CMakeLists.txt | 23 ++- heload/include/heload/heload.h | 20 ++- heload/include/heload/symbols.h | 22 +++ heload/src/auditor/auditor-x86_64.c | 81 +++++++++ heload/src/auditor/auditor.h | 26 +++ heload/src/heload.c | 265 +++++++++++++++++++++++++++- heload/src/symbols.c | 115 ++++++++++++ hiisi/hiisi.cpp | 6 +- hiisi/hiisi.h | 28 ++- hiisi/main.cpp | 95 ++-------- 12 files changed, 622 insertions(+), 111 deletions(-) create mode 100644 cmake/clang-toolchain.cmake create mode 100644 heload/include/heload/symbols.h create mode 100644 heload/src/auditor/auditor-x86_64.c create mode 100644 heload/src/auditor/auditor.h create mode 100644 heload/src/symbols.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b1f67c..3c84370 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,14 @@ cmake_minimum_required(VERSION 3.21) project(Hiisi) -# I just like to have this with my tooling +# Required for hiload set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + + set(CMAKE_CXX_STANDARD 17) +set(CMAKE_C) + +set(CMAKE_C_STANDARD 11) add_subdirectory(3rd/SDL EXCLUDE_FROM_ALL) add_subdirectory(heload) @@ -14,4 +19,4 @@ target_link_options(SDL3-shared PRIVATE -Wl,-z,nodelete) add_executable(hiisi-run hiisi/main.cpp) -target_link_libraries(hiisi-run heload) +target_link_libraries(hiisi-run hiisi-engine heload) diff --git a/cmake/clang-toolchain.cmake b/cmake/clang-toolchain.cmake new file mode 100644 index 0000000..10af68b --- /dev/null +++ b/cmake/clang-toolchain.cmake @@ -0,0 +1,43 @@ +# clang-toolchain.cmake - Complete toolchain file for Clang + +# Specify the compiler +set(CMAKE_C_COMPILER clang) +set(CMAKE_CXX_COMPILER clang++) + +# Specify the target system +# set(CMAKE_SYSTEM_NAME Linux) # Change to appropriate system (Linux, Darwin, Windows, etc.) + +# Specify LLVM binutils +set(CMAKE_AR llvm-ar CACHE FILEPATH "Archive command") +set(CMAKE_NM llvm-nm CACHE FILEPATH "Name mangling command") +set(CMAKE_RANLIB llvm-ranlib CACHE FILEPATH "Archive indexer command") +set(CMAKE_OBJDUMP llvm-objdump CACHE FILEPATH "Object dump command") +set(CMAKE_OBJCOPY llvm-objcopy CACHE FILEPATH "Object copy command") +set(CMAKE_STRIP llvm-strip CACHE FILEPATH "Strip command") +set(CMAKE_LINKER lld CACHE FILEPATH "Linker command") + +# Use lld as the linker +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fuse-ld=lld") +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=lld") + +# Set linker flags +# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=lld") +# set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=lld") +# set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -fuse-ld=lld") + +# Use Clang's libc++ instead of GCC's libstdc++ (optional) +# Uncomment these lines if you want to use libc++ +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") +# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi") +# set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -stdlib=libc++ -lc++abi") + +# Set compiler optimization flags (optional) +# set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") +# set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") +# set(CMAKE_C_FLAGS_DEBUG "-g -O0") +# set(CMAKE_CXX_FLAGS_DEBUG "-g -O0") + +# Enable address sanitizer (optional) +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") +# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") diff --git a/heload/CMakeLists.txt b/heload/CMakeLists.txt index b039eb0..132dee0 100644 --- a/heload/CMakeLists.txt +++ b/heload/CMakeLists.txt @@ -3,12 +3,19 @@ project(Heload) # I just like to have this with my tooling set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD 11) + +# heload library +# ############## add_library(heload SHARED src/heload.c + src/symbols.c ) +set_property(TARGET heload PROPERTY POSITION_INDEPENDENT_CODE ON) +target_link_libraries(heload dl) + # Specify the public headers location target_include_directories(heload PUBLIC $ # During build @@ -32,3 +39,17 @@ install(EXPORT heloadTargets ) export(TARGETS heload FILE heloadConfig.cmake) + +# auditor libraries +# ############### + +add_library(auditor-x86_64 SHARED + src/auditor/auditor-x86_64.c +) + +install(TARGETS auditor-x86_64 + EXPORT auditor-x86_64Targets + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) diff --git a/heload/include/heload/heload.h b/heload/include/heload/heload.h index 3b65fac..bfed347 100644 --- a/heload/include/heload/heload.h +++ b/heload/include/heload/heload.h @@ -1,7 +1,25 @@ #ifndef HELOAD_H_ #define HELOAD_H_ -void print_module_info(const char *module_name); +#ifdef __cplusplus +extern "C" { +#endif +// Return codes for reload_module +typedef enum { + RELOAD_SUCCESS = 0, + RELOAD_NOT_FOUND, + RELOAD_CLOSE_ERROR, + RELOAD_OPEN_ERROR +} ReloadResult; + +int he_init(); +void he_deinit(); +void he_print_module_infos(); +ReloadResult he_reload_module(const char *module_name); + +#ifdef __cplusplus +} +#endif #endif // HELOAD_H_ diff --git a/heload/include/heload/symbols.h b/heload/include/heload/symbols.h new file mode 100644 index 0000000..c7a533e --- /dev/null +++ b/heload/include/heload/symbols.h @@ -0,0 +1,22 @@ +#ifndef SYMBOLS_H_ +#define SYMBOLS_H_ + +#include +#include +#include + +typedef struct { + char **names; + void **addresses; + size_t count; + size_t capacity; +} SymbolInfos; + +typedef enum { CREATE_SUCCESS = 0, CREATE_FAILED } CreateResult; + +struct dl_phdr_info; + +CreateResult he_create_symbol_info(SymbolInfos *, struct dl_phdr_info *); +void he_free_symbol_info(SymbolInfos *); + +#endif // SYMBOLS_H_ diff --git a/heload/src/auditor/auditor-x86_64.c b/heload/src/auditor/auditor-x86_64.c new file mode 100644 index 0000000..d191884 --- /dev/null +++ b/heload/src/auditor/auditor-x86_64.c @@ -0,0 +1,81 @@ +#define _GNU_SOURCE + +#include +#include + +unsigned int la_version(unsigned int version) { + printf("la_version(): version = %u; LAV_CURRENT = %u\n", version, + LAV_CURRENT); + + return LAV_CURRENT; +} + +char *la_objsearch(const char *name, uintptr_t *cookie, unsigned int flag) { + printf("la_objsearch(): name = %s; cookie = %p", name, cookie); + printf("; flag = %s\n", (flag == LA_SER_ORIG) ? "LA_SER_ORIG" + : (flag == LA_SER_LIBPATH) ? "LA_SER_LIBPATH" + : (flag == LA_SER_RUNPATH) ? "LA_SER_RUNPATH" + : (flag == LA_SER_DEFAULT) ? "LA_SER_DEFAULT" + : (flag == LA_SER_CONFIG) ? "LA_SER_CONFIG" + : (flag == LA_SER_SECURE) ? "LA_SER_SECURE" + : "???"); + + return (char *)name; +} + +void la_activity(uintptr_t *cookie, unsigned int flag) { + printf("la_activity(): cookie = %p; flag = %s\n", cookie, + (flag == LA_ACT_CONSISTENT) ? "LA_ACT_CONSISTENT" + : (flag == LA_ACT_ADD) ? "LA_ACT_ADD" + : (flag == LA_ACT_DELETE) ? "LA_ACT_DELETE" + : "???"); +} + +unsigned int la_objopen(struct link_map *map, Lmid_t lmid, uintptr_t *cookie) { + printf("la_objopen(): loading \"%s\"; lmid = %s; cookie=%p\n", map->l_name, + (lmid == LM_ID_BASE) ? "LM_ID_BASE" + : (lmid == LM_ID_NEWLM) ? "LM_ID_NEWLM" + : "???", + cookie); + + return LA_FLG_BINDTO | LA_FLG_BINDFROM; +} + +unsigned int la_objclose(uintptr_t *cookie) { + printf("la_objclose(): %p\n", cookie); + + return 0; +} + +void la_preinit(uintptr_t *cookie) { printf("la_preinit(): %p\n", cookie); } + +uintptr_t la_symbind32(Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, unsigned int *flags, + const char *symname) { + printf("la_symbind64(): symname = %s; sym->st_value = %p\n", symname, + sym->st_value); + printf(" ndx = %u; flags = %#x", ndx, *flags); + printf("; refcook = %p; defcook = %p\n", refcook, defcook); + + return sym->st_value; +} + +uintptr_t la_symbind64(Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, unsigned int *flags, + const char *symname) { + printf("la_symbind64(): symname = %s; sym->st_value = %p\n", symname, + (void*)sym->st_value); + printf(" ndx = %u; flags = %#x", ndx, *flags); + printf("; refcook = %p; defcook = %p\n", refcook, defcook); + + return sym->st_value; +} + +Elf64_Addr la_i86_gnu_pltenter(Elf64_Sym *sym, unsigned int ndx, + uintptr_t *refcook, uintptr_t *defcook, + La_x86_64_regs *regs, unsigned int *flags, + const char *symname, long *framesizep) { + printf("la_x86_64_gnu_pltenter(): %s (%p)\n", symname, (void*)sym->st_value); + + return sym->st_value; +} diff --git a/heload/src/auditor/auditor.h b/heload/src/auditor/auditor.h new file mode 100644 index 0000000..c3349a3 --- /dev/null +++ b/heload/src/auditor/auditor.h @@ -0,0 +1,26 @@ +#ifndef AUDITOR_H_ +#define AUDITOR_H_ + +#define _GNU_SOURCE +#include +#include + +unsigned int la_version(unsigned int version); +char *la_objsearch(const char *name, uintptr_t *cookie, unsigned int flag); +void la_activity(uintptr_t *cookie, unsigned int flag); +unsigned int la_objopen(struct link_map *map, Lmid_t lmid, uintptr_t *cookie); +unsigned int la_objclose(uintptr_t *cookie); +void la_preinit(uintptr_t *cookie); +uintptr_t la_symbind32(Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, unsigned int *flags, + const char *symname); +uintptr_t la_symbind64(Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, unsigned int *flags, + const char *symname); +/* Elf32_Addr la_i86_gnu_pltenter(Elf32_Sym *sym, unsigned int ndx, */ +/* uintptr_t *refcook, uintptr_t *defcook, */ +/* La_i86_regs *regs, unsigned int *flags, */ +/* const char *symname, long *framesizep); */ + + +#endif // AUDITOR_H_ diff --git a/heload/src/heload.c b/heload/src/heload.c index 957d358..d4c6415 100644 --- a/heload/src/heload.c +++ b/heload/src/heload.c @@ -1,15 +1,266 @@ -#include "heload/heload.h" +// Required for dlinfo and other GNU specific linker code +#define _GNU_SOURCE -#include -#include +#include "heload/heload.h" +#include "heload/symbols.h" + +#include #include #include +#include +#include +#include +typedef struct { + char **names; // Array of library names + void **handles; // Array of library handles + SymbolInfos *symbols; // Symbol info for modules + size_t count; // Number of libraries + size_t capacity; // Allocated capacity +} ModuleInfos; -void print_module_info(const char *module_name) { - void *module = dlopen(module_name, RTLD_NOW); +static ModuleInfos *module_infos = 0; - struct Lmid_t *lmid; - dlinfo(module, RTLD_DI_LMID, lmid); +// Callback function for dl_iterate_phdr +static int gather_module_infos_callback(struct dl_phdr_info *info, size_t size, + void *data) { + ModuleInfos *infos = (ModuleInfos *)data; + // Resize arrays if needed + if (infos->count >= infos->capacity) { + infos->capacity *= 2; + char **new_names = realloc(infos->names, infos->capacity * sizeof(char *)); + void **new_handles = + realloc(infos->handles, infos->capacity * sizeof(void *)); + SymbolInfos *new_symbols = + realloc(infos->symbols, infos->capacity * sizeof(SymbolInfos)); + + if (!new_names || !new_handles || !new_symbols) { + return 1; // Stop iteration on error + } + + infos->names = new_names; + infos->handles = new_handles; + infos->symbols = new_symbols; + } + + // Store the module name + infos->names[infos->count] = strdup(info->dlpi_name); + if (!infos->names[infos->count]) { + return 1; // Stop iteration on error + } + + // Try to get the handle + infos->handles[infos->count] = + dlopen(info->dlpi_name, RTLD_LAZY | RTLD_NOLOAD); + + if (he_create_symbol_info(&infos->symbols[infos->count], info) != CREATE_SUCCESS) { + fprintf(stderr, "Failed to create symbol info for %s\n", infos->names[infos->count]); + } + infos->count++; + return 0; // Continue iteration +} + +static void free_module_infos(ModuleInfos *modules) { + if (!modules) + return; + + for (size_t i = 0; i < modules->count; i++) { + if (modules->names[i]) + free(modules->names[i]); + he_free_symbol_info(&modules->symbols[i]); + } + + free(modules->names); + free(modules->handles); + free(modules->symbols); + free(modules); +} + +static ModuleInfos *gather_shared_libraries(void) { + ModuleInfos *result = NULL; + + // Allocate the result structure + result = calloc(1, sizeof(ModuleInfos)); + if (!result) { + return NULL; + } + + // Initial capacity + result->capacity = 16; + result->names = calloc(result->capacity, sizeof(char *)); + result->handles = calloc(result->capacity, sizeof(void *)); + result->symbols = calloc(result->capacity, sizeof(SymbolInfos)); + + if (!result->names || !result->handles || !result->symbols) { + if (result->names) + free(result->names); + if (result->handles) + free(result->handles); + if (result->symbols) + free(result->symbols); + free(result); + return NULL; + } + + // Iterate over all shared objects + if (dl_iterate_phdr(gather_module_infos_callback, result) != 0) { + // Error occurred in callback + free_module_infos(result); + return NULL; + } + + return result; +} + +int he_init() { + assert(!module_infos); + + ModuleInfos *infos = gather_shared_libraries(); + if (!infos) { + fprintf(stderr, "Failed to gather module infos.\n"); + return 1; + } + if (module_infos) { + free_module_infos(module_infos); + } + module_infos = infos; + return 0; +} + +void he_deinit() { free_module_infos(module_infos); } + +/** + * Reloads a shared library module + * + * @param modules The ModuleInfos structure containing loaded modules + * @param filename The name of the module to reload + * @param updated_handle Pointer to store the new handle (can be NULL) + * @return ReloadResult indicating success or failure + */ +static ReloadResult reload_module(ModuleInfos *modules, const char *filename, + void **updated_handle) { + if (!modules || !filename) { + return RELOAD_NOT_FOUND; + } + + // Find the module by filename + int found = 0; + size_t index = 0; + + for (size_t i = 0; i < modules->count; i++) { + // Check if this is the module we're looking for + if (modules->names[i] && strcmp(modules->names[i], filename) == 0) { + found = 1; + index = i; + break; + } + + // Also check if the filename is at the end of the path + if (modules->names[i]) { + const char *basename = strrchr(modules->names[i], '/'); + if (basename && strcmp(basename + 1, filename) == 0) { + found = 1; + index = i; + break; + } + } + } + + if (!found) { + return RELOAD_NOT_FOUND; + } + + // Save the full path + char *fullpath = strdup(modules->names[index]); + if (!fullpath) { + return RELOAD_OPEN_ERROR; + } + + // Close the old handle + if (modules->handles[index]) { + if (dlclose(modules->handles[index]) != 0) { + free(fullpath); + return RELOAD_CLOSE_ERROR; + } + } + + // Open the module again with RTLD_NOW + void *new_handle = dlopen(fullpath, RTLD_NOW); + if (!new_handle) { + fprintf(stderr, "Error reloading module: %s\n", dlerror()); + free(fullpath); + return RELOAD_OPEN_ERROR; + } + + // Update the handle in our structure + modules->handles[index] = new_handle; + + // If the caller wants the new handle, provide it + if (updated_handle) { + *updated_handle = new_handle; + } + + free(fullpath); + return RELOAD_SUCCESS; +} +/** + * Helper function to print the result of a module reload + */ +static void print_reload_result(ReloadResult result, const char *filename) { + switch (result) { + case RELOAD_SUCCESS: + printf("Successfully reloaded module: %s\n", filename); + break; + case RELOAD_NOT_FOUND: + printf("Module not found: %s\n", filename); + break; + case RELOAD_CLOSE_ERROR: + printf("Error closing module: %s\n", filename); + break; + case RELOAD_OPEN_ERROR: + printf("Error reopening module: %s\n", filename); + break; + default: + printf("Unknown error reloading module: %s\n", filename); + } +} + +ReloadResult he_reload_module(const char *module_name) { + assert(module_infos); + + void *new_handle = NULL; + ReloadResult result = reload_module(module_infos, module_name, &new_handle); + print_reload_result(result, module_name); + + return result; +} + +void he_print_module_infos() { + assert(module_infos); + + const ModuleInfos *modules = module_infos; + if (!modules) { + printf("No module information available.\n"); + return; + } + + printf("Found %zu loaded modules:\n\n", modules->count); + + for (size_t i = 0; i < modules->count; i++) { + // Get handle information where possible + Dl_info info = {0}; + int has_info = 0; + + printf("%s: %p\n", modules->names[i], modules->handles[i]); + if (modules->symbols) { + for (int j = 0; j < modules->symbols->count; j++) { + const void *addr = modules->symbols->addresses[j]; + const char *name = modules->symbols->names[j]; + printf(" %p: %s\n", addr, name); + } + } + + printf("\n"); + } } diff --git a/heload/src/symbols.c b/heload/src/symbols.c new file mode 100644 index 0000000..c0c3dfb --- /dev/null +++ b/heload/src/symbols.c @@ -0,0 +1,115 @@ +#define _GNU_SOURCE +#include "heload/symbols.h" + +#include +#include +#include +#include +#include +#include +#include + +/** + * Gathers and populates symbols, given a dynamic module info + * + * Will clear and free the given SymbolInfo struct. Allocates enough memory to + * hold found symbols. + */ +CreateResult he_create_symbol_info(SymbolInfos *symbols, + struct dl_phdr_info *info) { + + if (!symbols) + return CREATE_FAILED; + + he_free_symbol_info(symbols); + + for (int i = 0; i < info->dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &info->dlpi_phdr[i]; + + // Look for the dynamic segment + if (phdr->p_type != PT_DYNAMIC) + continue; + + printf("Dynamic Header:\n"); + printf("p_type: %u\n", phdr->p_type); + printf("p_flags: %u\n", phdr->p_flags); + printf("p_offset: %#06x\n", phdr->p_offset); + printf("p_vaddr: %#06x\n", phdr->p_vaddr); + printf("p_paddr: %#06x\n", phdr->p_paddr); + printf("p_filesz: %zu\n", phdr->p_filesz); + printf("p_memsz: %zu\n", phdr->p_memsz); + printf("p_align: %zu\n", phdr->p_align); + + const ElfW(Dyn) *dyn = (const ElfW(Dyn) *)(info->dlpi_addr + phdr->p_vaddr); + const char *strtab = NULL; + const ElfW(Sym) *symtab = NULL; + size_t symtab_size = 0; + size_t strtab_size = 0; + + // Parse the dynamic table + for (; dyn->d_tag != DT_NULL; dyn++) { + if (dyn->d_tag == DT_STRTAB) { + strtab = (const char *)(dyn->d_un.d_ptr); + } else if (dyn->d_tag == DT_STRSZ) { + strtab_size = dyn->d_un.d_val; + } else if (dyn->d_tag == DT_SYMTAB) { + symtab = (const ElfW(Sym) *)(dyn->d_un.d_ptr); + } else if (dyn->d_tag == DT_SYMENT) { + symtab_size = dyn->d_un.d_val; + } + } + + // Ensure we found the symbol and string tables + if (!strtab || !symtab || strtab_size == 0 || symtab_size == 0) { + fprintf(stderr, "Failed to find symbol or string table in %s\n", + info->dlpi_name); + return CREATE_FAILED; + } + + symbols->capacity = symtab_size / sizeof(ElfW(Sym)); + + symbols->names = calloc(symbols->capacity, sizeof(char *)); + if (!symbols->names) { + fprintf(stderr, "Failed to allocate memory for symbol names.\n"); + return CREATE_FAILED; + } + + symbols->addresses = calloc(symbols->capacity, sizeof(void *)); + if (!symbols->addresses) { + fprintf(stderr, "Failed to allocate memory for symbol addresses.\n"); + return CREATE_FAILED; + } + + // Iterate over the symbol table + for (const ElfW(Sym) *sym = symtab; + (const char *)sym < (const char *)symtab + symtab_size; sym++) { + if (ELF64_ST_TYPE(sym->st_info) == STT_FUNC || + ELF64_ST_TYPE(sym->st_info) == STT_OBJECT) { + const char *name = strdup(&strtab[sym->st_name]); + void *address = (void *)(info->dlpi_addr + sym->st_value); + + // Store the symbol information in the struct of arrays + if (symbols->count < symbols->capacity) { + symbols->names[symbols->count] = (char *)name; + symbols->addresses[symbols->count] = address; + symbols->count++; + } else { + fprintf(stderr, "Symbol table capacity exceeded!\n"); + return CREATE_FAILED; + } + } + } + } + + return CREATE_SUCCESS; +} + +void he_free_symbol_info(SymbolInfos *symbols) { + for (size_t i = 0; i < symbols->count; i++) { + free(symbols->names[i]); + } + free(symbols->names); + free(symbols->addresses); + symbols->count = 0; + symbols->capacity = 0; +} diff --git a/hiisi/hiisi.cpp b/hiisi/hiisi.cpp index ddc819d..4d0efd1 100644 --- a/hiisi/hiisi.cpp +++ b/hiisi/hiisi.cpp @@ -6,6 +6,7 @@ #define WINDOW_WIDTH 1280 #define WINDOW_HEIGHT 720 +namespace hiisi { /* This function runs once at startup. */ int init(EngineData *state, int argc, char *argv[]) { @@ -52,7 +53,7 @@ int iterate(EngineData *state) /* we'll have the rectangles grow and shrink over a few seconds. */ const float direction = ((now % 2000) >= 1000) ? 1.0f : -1.0f; - const float scale = ((float) (((int) (now % 1000)) - 500) / 500.0f) * direction; + const float scale = ((float) (((int) (now % 1000)) - 500) / 50.0f) * direction; /* as you can see from this, rendering draws over whatever was drawn before it. */ SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); /* black, full alpha */ @@ -64,7 +65,7 @@ int iterate(EngineData *state) pretty standard in 2D graphics. */ /* Let's draw a single rectangle (square, really). */ - rects[0].x = rects[0].y = 100; + rects[0].x = rects[0].y = 10; rects[0].w = rects[0].h = 100 + (100 * scale); SDL_SetRenderDrawColor(renderer, 250, 0, 255, SDL_ALPHA_OPAQUE); /* red, full alpha */ SDL_RenderRect(renderer, &rects[0]); @@ -110,3 +111,4 @@ void quit(EngineData *state) { /* SDL will clean up the window/renderer for us. */ } +} diff --git a/hiisi/hiisi.h b/hiisi/hiisi.h index 3d5510a..93bca23 100644 --- a/hiisi/hiisi.h +++ b/hiisi/hiisi.h @@ -1,25 +1,17 @@ #ifndef HIISI_H_ #define HIISI_H_ -extern "C" { +namespace hiisi { - struct EngineData { - void *window = 0; - void *renderer = 0; - }; +struct EngineData { + void *window = 0; + void *renderer = 0; +}; - struct Engine { - void *module = 0; - int (*init)(EngineData *, int, char**); - int (*event)(EngineData *); - int (*iterate)(EngineData *); - void (*quit)(EngineData *); - }; +int init(EngineData *state, int argc, char *argv[]); +int event(EngineData *state); +int iterate(EngineData *state); +void quit(EngineData *state); - int init(EngineData *state, int argc, char *argv[]); - int event(EngineData *state); - int iterate(EngineData *state); - void quit(EngineData *state); - -} +} // namespace hiisi #endif // HIISI_H_ diff --git a/hiisi/main.cpp b/hiisi/main.cpp index 313740f..6a26e01 100644 --- a/hiisi/main.cpp +++ b/hiisi/main.cpp @@ -15,101 +15,36 @@ void reload_signal_handler(int) { reload_requested = true; } -Engine load_hiisi() { - const char *name = "build/hiisi/libhiisi-engine.so"; - void *mod = dlopen(name, RTLD_NOW); - - Lmid_t list; - dlinfo(mod, RTLD_DI_LMID, &list); - printf("link-map list id: %ld\n", list); - - link_map *lmap = 0; - dlinfo(mod, RTLD_DI_LINKMAP, &lmap); - - printf("Link map:\n"); - printf("addr: %lu\n", lmap->l_addr); - printf("name: %s\n", lmap->l_name); - printf("dynamic section: %p\n", lmap->l_ld); - - char pathname[256]; - dlinfo(mod, RTLD_DI_ORIGIN, pathname); - printf("path: %s\n", pathname); - - /* Discover the size of the buffer that we must pass to - RTLD_DI_SERINFO. */ - Dl_serinfo serinfo; - Dl_serinfo *sip; - - if (dlinfo(mod, RTLD_DI_SERINFOSIZE, &serinfo) == -1) { - fprintf(stderr, "RTLD_DI_SERINFOSIZE failed: %s\n", dlerror()); +void reload_modules() { + if (he_reload_module("libhiisi-engine.so") != 0) { + fprintf(stderr, "Failed to reload libhiisi-engine.so. Exiting.\n"); exit(EXIT_FAILURE); } - - /* Allocate the buffer for use with RTLD_DI_SERINFO. */ - - sip = (Dl_serinfo*)malloc(serinfo.dls_size); - if (sip == NULL) { - perror("malloc"); - exit(EXIT_FAILURE); - } - - /* Initialize the 'dls_size' and 'dls_cnt' fields in the newly - allocated buffer. */ - - if (dlinfo(mod, RTLD_DI_SERINFOSIZE, sip) == -1) { - fprintf(stderr, "RTLD_DI_SERINFOSIZE failed: %s\n", dlerror()); - exit(EXIT_FAILURE); - } - - /* Fetch and print library search list. */ - - if (dlinfo(mod, RTLD_DI_SERINFO, sip) == -1) { - fprintf(stderr, "RTLD_DI_SERINFO failed: %s\n", dlerror()); - exit(EXIT_FAILURE); - } - - for (size_t j = 0; j < serinfo.dls_cnt; j++) - printf("dls_serpath[%zu].dls_name = %s\n", - j, sip->dls_serpath[j].dls_name); - - Engine engine; - engine.module = mod; - *(void **)&engine.init = dlsym(mod, "init"); - *(void **)&engine.event = dlsym(mod, "event"); - *(void **)&engine.iterate = dlsym(mod, "iterate"); - *(void **)&engine.quit = dlsym(mod, "quit"); - - assert(engine.init); - assert(engine.event); - assert(engine.iterate); - assert(engine.quit); - - return engine; } int main(int argc, char **argv) { signal(SIGUSR1, reload_signal_handler); - EngineData state; - - Engine hiisi = load_hiisi(); - if (!hiisi.module) { - printf("%s\n", dlerror()); - return 1; + if (he_init() != 0) { + fprintf(stderr, "Failed to initialize heload. Exiting.\n"); + exit(EXIT_FAILURE); } - hiisi.init(&state, argc, argv); - while (hiisi.event(&state)) { - hiisi.iterate(&state); + he_print_module_infos(); + + hiisi::EngineData state; + + hiisi::init(&state, argc, argv); + while (hiisi::event(&state)) { + hiisi::iterate(&state); if (reload_requested) { - dlclose(hiisi.module); - hiisi = load_hiisi(); + reload_modules(); reload_requested = false; } } - dlclose(hiisi.module); + he_deinit(); return 0; }