diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d981b4..066d27e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,6 +145,7 @@ add_library(memkit SHARED src/memory.c src/hooking.c src/il2cpp.c + src/xdl_wrapper.c ) target_include_directories(memkit PRIVATE diff --git a/Makefile b/Makefile index 5bbbd6c..cb8ad12 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ INCLUDES = -Iinclude \ LDFLAGS = -shared -llog -landroid -MEMKIT_SRCS = src/memory.c src/hooking.c src/il2cpp.c +MEMKIT_SRCS = src/memory.c src/hooking.c src/il2cpp.c src/xdl_wrapper.c SH_DIR = deps/shadowhook/shadowhook/src/main/cpp SH_SRCS = $(SH_DIR)/arch/$(SH_ARCH)/sh_inst.c \ $(SH_DIR)/arch/$(SH_ARCH)/sh_glue.S \ @@ -118,7 +118,7 @@ endef # Targets # ============================================================================ -.PHONY: all clean setup directories banner help +.PHONY: all clean setup directories banner help test test-clean all: banner directories $(LIB_DIR)/libmemkit.so @@ -157,6 +157,7 @@ directories: @mkdir -p $(OBJ_DIR)/shadowhook/common @mkdir -p $(OBJ_DIR)/shadowhook/nothing @mkdir -p $(OBJ_DIR)/shadowhook/third_party/xdl + @mkdir -p $(OBJ_DIR)/tests @mkdir -p $(LIB_DIR) $(LIB_DIR)/libmemkit.so: $(ALL_OBJS) @@ -188,3 +189,32 @@ clean: @echo "$(WHITE)Cleaning build directory...$(RESET)" @echo "" @rm -rf build/ + +# ============================================================================ +# Unit Tests +# ============================================================================ + +TEST_SRCS = tests/xdl_wrapper_test.c +TEST_OBJS = $(patsubst %.c,$(OBJ_DIR)/%.o,$(TEST_SRCS)) +TEST_BIN = $(LIB_DIR)/xdl_wrapper_test + +test: banner directories $(LIB_DIR)/libmemkit.so $(TEST_BIN) + @echo "" + @echo "$(WHITE)Test binary built successfully:$(RESET) $(TEST_BIN)" + @echo "$(YELLOW)Note:$(RESET) This is an Android binary. Push to device to run:" + @echo " adb push $(TEST_BIN) /data/local/tmp/" + @echo " adb shell /data/local/tmp/xdl_wrapper_test" + @echo "" + +$(TEST_BIN): $(TEST_OBJS) $(LIB_DIR)/libmemkit.so + @printf "$(WHITE)[TEST] $(YELLOW)[LD] $(WHITE)%s$(RESET)\n" "$@" + @$(CC) $(LDFLAGS) -o $@ $(TEST_OBJS) -L$(LIB_DIR) -lmemkit + +$(OBJ_DIR)/tests/%.o: tests/%.c + $(call update_progress) + $(call print_progress,"[CC]","$<",$(PERCENT)) + @$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +test-clean: + @echo "$(WHITE)Cleaning test binaries...$(RESET)" + @rm -f $(TEST_BIN) diff --git a/include/memkit.h b/include/memkit.h index 7dddd36..9f79f0f 100644 --- a/include/memkit.h +++ b/include/memkit.h @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -128,15 +129,222 @@ void* memkit_il2cpp_resolve_symtab(const char* symbol_name); void* memkit_il2cpp_get_handle(void); // ============================================================================ -// MACROS FOR CONVENIENCE +// XDL WRAPPER TYPES +// ============================================================================ + +/** + * Library information for iteration callbacks + */ +typedef struct { + const char* name; // Library basename (e.g., "libil2cpp.so") + const char* path; // Full path (may be NULL if not available) + uintptr_t base; // Load base address + size_t size; // Library size in bytes +} MemKitLibInfo; + +/** + * Callback type for library iteration + * @param info Library information + * @param user_data User context data + * @return true to continue iteration, false to stop + */ +typedef bool (*memkit_lib_iter_cb_t)(const MemKitLibInfo* info, void* user_data); + +/** + * Symbol information from address resolution + */ +typedef struct { + const char* lib_name; // Library pathname + uintptr_t lib_base; // Library base address + const char* sym_name; // Nearest symbol name (may be NULL) + uintptr_t sym_offset; // Offset from symbol start + size_t sym_size; // Symbol size in bytes +} MemKitSymInfo; + +/** + * Opaque context for address resolution cache + * Manages internal xdl_addr() cache to prevent memory leaks + */ +typedef struct memkit_addr_ctx memkit_addr_ctx_t; + +// ============================================================================ +// XDL WRAPPER API - PHASE 1: CORE DISCOVERY +// ============================================================================ + +/** + * Iterate all loaded shared libraries + * + * @param callback Function called for each library + * @param user_data User context passed to callback + * @param flags XDL_DEFAULT or XDL_FULL_PATHNAME + * @return Number of libraries iterated, or -1 on error + * + * Example: + * typedef struct { const char* target; uintptr_t base; } ctx_t; + * + * bool callback(const MemKitLibInfo* info, void* user_data) { + * ctx_t* ctx = (ctx_t*)user_data; + * if (strcmp(info->name, ctx->target) == 0) { + * ctx->base = info->base; + * return false; // Stop iteration + * } + * return true; // Continue + * } + * + * ctx_t ctx = {.target = "libil2cpp.so"}; + * memkit_xdl_iterate(callback, &ctx, XDL_DEFAULT); + */ +int memkit_xdl_iterate(memkit_lib_iter_cb_t callback, void* user_data, int flags); + +/** + * Open a handle to any loaded library + * + * @param name Library name (basename or full path) + * @param flags XDL_DEFAULT, XDL_TRY_FORCE_LOAD, or XDL_ALWAYS_FORCE_LOAD + * @return Handle on success, NULL on failure + * + * Example: + * void* handle = memkit_xdl_open("libc.so", XDL_DEFAULT); + * if (handle) { + * void* sym = memkit_xdl_sym(handle, "open", NULL); + * memkit_xdl_close(handle); + * } + */ +void* memkit_xdl_open(const char* name, int flags); + +/** + * Close a library handle + * + * @param handle Handle from memkit_xdl_open() + * @return true if handle was closed, false if no action needed + */ +bool memkit_xdl_close(void* handle); + +/** + * Resolve a symbol from a library handle (.dynsym section) + * + * @param handle Library handle from memkit_xdl_open() + * @param symbol Symbol name to resolve + * @param out_size Optional output: symbol size + * @return Symbol address on success, NULL on failure + * + * Example: + * void* handle = memkit_xdl_open("libc.so", XDL_DEFAULT); + * size_t size; + * void* open_sym = memkit_xdl_sym(handle, "open", &size); + */ +void* memkit_xdl_sym(void* handle, const char* symbol, size_t* out_size); + +/** + * Resolve a debug symbol from a library handle (.symtab section) + * Use for stripped/internal symbols not in .dynsym + * + * @param handle Library handle from memkit_xdl_open() + * @param symbol Symbol name to resolve + * @param out_size Optional output: symbol size + * @return Symbol address on success, NULL on failure + */ +void* memkit_xdl_dsym(void* handle, const char* symbol, size_t* out_size); + +/** + * Get detailed information about a loaded library + * + * @param handle Library handle from memkit_xdl_open() + * @param out Output: library information + * @return true on success, false on failure + * + * Example: + * MemKitLibInfo info; + * if (memkit_xdl_get_lib_info(handle, &info)) { + * // info.base, info.size available + * } + */ +bool memkit_xdl_get_lib_info(void* handle, MemKitLibInfo* out); + +// ============================================================================ +// XDL WRAPPER API - PHASE 2: DEBUG INTROSPECTION +// ============================================================================ + +/** + * Create address resolution context + * Must be destroyed with memkit_xdl_addr_ctx_destroy() to prevent memory leaks + * + * @return Context pointer, or NULL on failure + * + * Example: + * memkit_addr_ctx_t* ctx = memkit_xdl_addr_ctx_create(); + * // ... use context for multiple lookups ... + * memkit_xdl_addr_ctx_destroy(ctx); + */ +memkit_addr_ctx_t* memkit_xdl_addr_ctx_create(void); + +/** + * Destroy address resolution context and free cache + * + * @param ctx Context pointer from memkit_xdl_addr_ctx_create() + */ +void memkit_xdl_addr_ctx_destroy(memkit_addr_ctx_t* ctx); + +/** + * Resolve address to symbol information + * + * @param addr Target address to resolve + * @param out Output: symbol information + * @param ctx Context from memkit_xdl_addr_ctx_create() + * @return true on success, false on failure + * + * Example: + * memkit_addr_ctx_t* ctx = memkit_xdl_addr_ctx_create(); + * MemKitSymInfo info; + * if (memkit_xdl_addr_to_symbol((void*)0x12345678, &info, ctx)) { + * // info.sym_name contains nearest symbol + * } + * memkit_xdl_addr_ctx_destroy(ctx); + */ +bool memkit_xdl_addr_to_symbol(void* addr, MemKitSymInfo* out, memkit_addr_ctx_t* ctx); + +/** + * Resolve address to symbol with flags (advanced) + * + * @param addr Target address + * @param out Output: symbol information + * @param ctx Context pointer (may be NULL for single lookup) + * @param flags XDL_DEFAULT or XDL_NON_SYM (skip symbol lookup) + * @return true on success, false on failure + * + * Use XDL_NON_SYM for fast address-to-library lookup without symbol resolution. + */ +bool memkit_xdl_addr_to_symbol4(void* addr, MemKitSymInfo* out, memkit_addr_ctx_t* ctx, int flags); + +// ============================================================================ +// XDL WRAPPER API - PHASE 3: ADVANCED FEATURES +// ============================================================================ + +/** + * Create a handle from dl_phdr_info (advanced use case) + * Typically used during library iteration callbacks + * + * @param info Pointer to dl_phdr_info structure + * @return Handle on success, NULL on failure + * + * Example: + * bool callback(const MemKitLibInfo* info, void* user_data) { + * // Create handle without knowing library name + * void* handle = memkit_xdl_open_from_phdr(info->phdr_info); + * } + */ +void* memkit_xdl_open_from_phdr(struct dl_phdr_info* info); + +// ============================================================================ +// CONVENIENCE MACROS // ============================================================================ /** * IL2CPP_CALL macro - Auto-caches resolved function pointer * Uses __builtin_expect for branch prediction optimization - * + * * Usage: IL2CPP_CALL(return_type, "symbol_name", arg_types...)(arguments...) - * + * * Example: * void* domain = IL2CPP_CALL(void*, "il2cpp_domain_get")(void); * IL2CPP_CALL(void, "il2cpp_thread_attach", void*)(domain); @@ -149,6 +357,27 @@ void* memkit_il2cpp_get_handle(void); func_ptr; \ }) +/** + * XDL_RESOLVE - Resolve symbol from library (one-shot, auto-closes handle) + * + * Usage: void* sym = XDL_RESOLVE("libc.so", "open"); + * + * Note: This is a convenience macro for quick one-off lookups. + * For multiple lookups, use memkit_xdl_open() + memkit_xdl_sym() directly. + */ +#define XDL_RESOLVE(lib_name, symbol) \ + memkit_xdl_sym(memkit_xdl_open(lib_name, XDL_DEFAULT), symbol, NULL) + +/** + * XDL_RESOLVE_SIZE - Resolve symbol with size output + * + * Usage: + * size_t size; + * void* sym = XDL_RESOLVE_SIZE("libc.so", "open", &size); + */ +#define XDL_RESOLVE_SIZE(lib_name, symbol, out_size) \ + memkit_xdl_sym(memkit_xdl_open(lib_name, XDL_DEFAULT), symbol, out_size) + #ifdef __cplusplus } #endif diff --git a/src/xdl_wrapper.c b/src/xdl_wrapper.c new file mode 100644 index 0000000..fb3afe9 --- /dev/null +++ b/src/xdl_wrapper.c @@ -0,0 +1,232 @@ +// +// Android-Mem-Kit: xDL Wrapper Layer +// Generic dynamic linking toolkit for library discovery and symbol resolution +// + +#include +#include +#include +#include +#include +#include + +#include "memkit.h" +#include "xdl.h" + +// ============================================================================ +// PHASE 1: CORE DISCOVERY API +// ============================================================================ + +// Internal callback adapter for library iteration +typedef struct { + memkit_lib_iter_cb_t user_callback; + void* user_data; + int flags; + int count; +} scan_callback_ctx_t; + +// Thread-local storage for basename buffer (prevents race conditions) +// Each thread gets its own buffer, making memkit_xdl_iterate() thread-safe +__thread static char tls_basename_buf[PATH_MAX]; + +static int scan_callback_adapter(struct dl_phdr_info* info, size_t size, void* data) { + (void)size; // Unused parameter + scan_callback_ctx_t* ctx = (scan_callback_ctx_t*)data; + + MemKitLibInfo lib_info = {0}; + + // Extract basename if not full pathname + // Note: basename() may modify its input, so we use thread-local buffer + if (ctx->flags & XDL_FULL_PATHNAME) { + lib_info.path = info->dlpi_name; + if (info->dlpi_name && info->dlpi_name[0] != '\0') { + strncpy(tls_basename_buf, info->dlpi_name, sizeof(tls_basename_buf) - 1); + tls_basename_buf[sizeof(tls_basename_buf) - 1] = '\0'; + lib_info.name = basename(tls_basename_buf); + } else { + lib_info.name = NULL; + } + } else { + lib_info.name = info->dlpi_name; + lib_info.path = NULL; + } + + lib_info.base = info->dlpi_addr; + + // Calculate size from program headers + lib_info.size = 0; + for (size_t i = 0; i < info->dlpi_phnum; i++) { + if (info->dlpi_phdr[i].p_type == PT_LOAD) { + lib_info.size += info->dlpi_phdr[i].p_memsz; + } + } + + // Call user callback + // xdl_iterate_phdr: return 0 to continue, non-zero to stop + // Our callback: return true to continue, false to stop + if (ctx->user_callback(&lib_info, ctx->user_data)) { + ctx->count++; + return 0; // Continue iteration + } else { + return 1; // Stop iteration + } +} + +int memkit_xdl_iterate(memkit_lib_iter_cb_t callback, void* user_data, int flags) { + if (!callback) { + return -1; + } + + // Validate flags - only XDL_DEFAULT and XDL_FULL_PATHNAME are valid + if (flags != XDL_DEFAULT && flags != XDL_FULL_PATHNAME) { + return -1; + } + + scan_callback_ctx_t ctx = { + .user_callback = callback, + .user_data = user_data, + .flags = flags, + .count = 0 + }; + + int result = xdl_iterate_phdr(scan_callback_adapter, &ctx, flags); + return (result < 0) ? -1 : ctx.count; +} + +void* memkit_xdl_open(const char* name, int flags) { + if (!name) { + return NULL; + } + return xdl_open(name, flags); +} + +bool memkit_xdl_close(void* handle) { + if (!handle) { + return false; + } + return xdl_close(handle) == 0; // xdl_close returns 0 on success +} + +void* memkit_xdl_sym(void* handle, const char* symbol, size_t* out_size) { + if (!handle || !symbol) { + return NULL; + } + return xdl_sym(handle, symbol, out_size); +} + +void* memkit_xdl_dsym(void* handle, const char* symbol, size_t* out_size) { + if (!handle || !symbol) { + return NULL; + } + return xdl_dsym(handle, symbol, out_size); +} + +bool memkit_xdl_get_lib_info(void* handle, MemKitLibInfo* out) { + if (!handle || !out) { + return false; + } + + xdl_info_t info = {0}; + + int result = xdl_info(handle, XDL_DI_DLINFO, &info); + if (result != 0) { + return false; + } + + out->name = info.dli_fname ? info.dli_fname : ""; + out->path = info.dli_fname ? info.dli_fname : ""; + out->base = (uintptr_t)info.dli_fbase; + + // Note: phdr/phnum not exposed in this version for simplicity + // Can be added later if needed + + return true; +} + +// ============================================================================ +// PHASE 2: DEBUG INTROSPECTION API +// ============================================================================ + +// Internal structure (opaque to users) +struct memkit_addr_ctx { + void* cache; // Internal xdl_addr() cache +}; + +memkit_addr_ctx_t* memkit_xdl_addr_ctx_create(void) { + memkit_addr_ctx_t* ctx = (memkit_addr_ctx_t*)calloc(1, sizeof(memkit_addr_ctx_t)); + if (!ctx) { + return NULL; + } + ctx->cache = NULL; // Initialize to NULL (first xdl_addr call will allocate) + return ctx; +} + +void memkit_xdl_addr_ctx_destroy(memkit_addr_ctx_t* ctx) { + if (!ctx) { + return; + } + + // Clean xdl_addr cache to prevent memory leak + if (ctx->cache) { + xdl_addr_clean(&ctx->cache); + } + + free(ctx); +} + +bool memkit_xdl_addr_to_symbol(void* addr, MemKitSymInfo* out, memkit_addr_ctx_t* ctx) { + if (!addr || !out) { + return false; + } + + xdl_info_t info = {0}; + + int result = xdl_addr(addr, &info, (ctx) ? &ctx->cache : NULL); + if (result == 0) { + return false; + } + + out->lib_name = info.dli_fname ? info.dli_fname : ""; + out->lib_base = (uintptr_t)info.dli_fbase; + out->sym_name = info.dli_sname; // May be NULL if no symbol found + out->sym_offset = info.dli_saddr + ? (uintptr_t)addr - (uintptr_t)info.dli_saddr + : 0; + out->sym_size = info.dli_ssize; + + return true; +} + +bool memkit_xdl_addr_to_symbol4(void* addr, MemKitSymInfo* out, memkit_addr_ctx_t* ctx, int flags) { + if (!addr || !out) { + return false; + } + + xdl_info_t info = {0}; + + int result = xdl_addr4(addr, &info, (ctx) ? &ctx->cache : NULL, flags); + if (result == 0) { + return false; + } + + out->lib_name = info.dli_fname ? info.dli_fname : ""; + out->lib_base = (uintptr_t)info.dli_fbase; + out->sym_name = info.dli_sname; // May be NULL if no symbol found + out->sym_offset = info.dli_saddr + ? (uintptr_t)addr - (uintptr_t)info.dli_saddr + : 0; + out->sym_size = info.dli_ssize; + + return true; +} + +// ============================================================================ +// PHASE 3: ADVANCED FEATURES +// ============================================================================ + +void* memkit_xdl_open_from_phdr(struct dl_phdr_info* info) { + if (!info) { + return NULL; + } + return xdl_open2(info); +} diff --git a/tests/xdl_wrapper_test.c b/tests/xdl_wrapper_test.c new file mode 100644 index 0000000..289157a --- /dev/null +++ b/tests/xdl_wrapper_test.c @@ -0,0 +1,363 @@ +// +// Android-Mem-Kit: xDL Wrapper Unit Tests +// Basic unit tests for xdl_wrapper module +// + +#include +#include +#include + +#include "memkit.h" +#include "xdl.h" + +// ============================================================================ +// Test Counters +// ============================================================================ + +static int g_tests_run = 0; +static int g_tests_passed = 0; +static int g_tests_failed = 0; + +#define TEST(name) \ + static void test_##name(void); \ + static void run_test_##name(void) { \ + g_tests_run++; \ + printf("[TEST] %s... ", #name); \ + fflush(stdout); \ + test_##name(); \ + g_tests_passed++; \ + printf("PASSED\n"); \ + } \ + static void test_##name(void) + +#define RUN_TEST(name) run_test_##name() + +// ============================================================================ +// Test Callbacks +// ============================================================================ + +static bool dummy_callback(const MemKitLibInfo* info, void* user_data) { + (void)info; + (void)user_data; + return true; +} + +static bool find_libc_callback(const MemKitLibInfo* info, void* user_data) { + bool* found = (bool*)user_data; + if (info->name && strstr(info->name, "libc.so") != NULL) { + *found = true; + return false; // Stop iteration + } + return true; // Continue +} + +// ============================================================================ +// Test: memkit_xdl_iterate with NULL callback +// ============================================================================ + +TEST(memkit_xdl_iterate_null_callback) { + int result = memkit_xdl_iterate(NULL, NULL, XDL_DEFAULT); + assert(result == -1); +} + +// ============================================================================ +// Test: memkit_xdl_iterate with invalid flags +// ============================================================================ + +TEST(memkit_xdl_iterate_invalid_flags) { + int result = memkit_xdl_iterate(dummy_callback, NULL, 0xFF); + assert(result == -1); +} + +// ============================================================================ +// Test: memkit_xdl_open with NULL name +// ============================================================================ + +TEST(memkit_xdl_open_null_name) { + void* handle = memkit_xdl_open(NULL, XDL_DEFAULT); + assert(handle == NULL); +} + +// ============================================================================ +// Test: memkit_xdl_close with NULL handle +// ============================================================================ + +TEST(memkit_xdl_close_null_handle) { + bool result = memkit_xdl_close(NULL); + assert(result == false); +} + +// ============================================================================ +// Test: memkit_xdl_sym with NULL handle +// ============================================================================ + +TEST(memkit_xdl_sym_null_handle) { + void* sym = memkit_xdl_sym(NULL, "open", NULL); + assert(sym == NULL); +} + +// ============================================================================ +// Test: memkit_xdl_sym with NULL symbol +// ============================================================================ + +TEST(memkit_xdl_sym_null_symbol) { + void* handle = memkit_xdl_open("libc.so", XDL_DEFAULT); + if (handle) { + void* sym = memkit_xdl_sym(handle, NULL, NULL); + assert(sym == NULL); + memkit_xdl_close(handle); + } +} + +// ============================================================================ +// Test: memkit_xdl_dsym with NULL handle +// ============================================================================ + +TEST(memkit_xdl_dsym_null_handle) { + void* sym = memkit_xdl_dsym(NULL, "open", NULL); + assert(sym == NULL); +} + +// ============================================================================ +// Test: memkit_xdl_dsym with NULL symbol +// ============================================================================ + +TEST(memkit_xdl_dsym_null_symbol) { + void* handle = memkit_xdl_open("libc.so", XDL_DEFAULT); + if (handle) { + void* sym = memkit_xdl_dsym(handle, NULL, NULL); + assert(sym == NULL); + memkit_xdl_close(handle); + } +} + +// ============================================================================ +// Test: memkit_xdl_get_lib_info with NULL handle +// ============================================================================ + +TEST(memkit_xdl_get_lib_info_null_handle) { + MemKitLibInfo info; + bool result = memkit_xdl_get_lib_info(NULL, &info); + assert(result == false); +} + +// ============================================================================ +// Test: memkit_xdl_get_lib_info with NULL output +// ============================================================================ + +TEST(memkit_xdl_get_lib_info_null_output) { + void* handle = memkit_xdl_open("libc.so", XDL_DEFAULT); + if (handle) { + bool result = memkit_xdl_get_lib_info(handle, NULL); + assert(result == false); + memkit_xdl_close(handle); + } +} + +// ============================================================================ +// Test: memkit_xdl_addr_ctx_create/destroy lifecycle +// ============================================================================ + +TEST(memkit_xdl_addr_ctx_lifecycle) { + memkit_addr_ctx_t* ctx = memkit_xdl_addr_ctx_create(); + assert(ctx != NULL); + memkit_xdl_addr_ctx_destroy(ctx); + // Should not crash when destroying NULL context twice + memkit_xdl_addr_ctx_destroy(NULL); +} + +// ============================================================================ +// Test: memkit_xdl_addr_to_symbol with NULL address +// ============================================================================ + +TEST(memkit_xdl_addr_to_symbol_null_addr) { + memkit_addr_ctx_t* ctx = memkit_xdl_addr_ctx_create(); + MemKitSymInfo info; + bool result = memkit_xdl_addr_to_symbol(NULL, &info, ctx); + assert(result == false); + memkit_xdl_addr_ctx_destroy(ctx); +} + +// ============================================================================ +// Test: memkit_xdl_addr_to_symbol with NULL output +// ============================================================================ + +TEST(memkit_xdl_addr_to_symbol_null_output) { + memkit_addr_ctx_t* ctx = memkit_xdl_addr_ctx_create(); + bool result = memkit_xdl_addr_to_symbol((void*)0x1234, NULL, ctx); + assert(result == false); + memkit_xdl_addr_ctx_destroy(ctx); +} + +// ============================================================================ +// Test: memkit_xdl_addr_to_symbol4 with NULL address +// ============================================================================ + +TEST(memkit_xdl_addr_to_symbol4_null_addr) { + memkit_addr_ctx_t* ctx = memkit_xdl_addr_ctx_create(); + MemKitSymInfo info; + bool result = memkit_xdl_addr_to_symbol4(NULL, &info, ctx, XDL_DEFAULT); + assert(result == false); + memkit_xdl_addr_ctx_destroy(ctx); +} + +// ============================================================================ +// Test: memkit_xdl_addr_to_symbol4 with NULL output +// ============================================================================ + +TEST(memkit_xdl_addr_to_symbol4_null_output) { + memkit_addr_ctx_t* ctx = memkit_xdl_addr_ctx_create(); + bool result = memkit_xdl_addr_to_symbol4((void*)0x1234, NULL, ctx, XDL_DEFAULT); + assert(result == false); + memkit_xdl_addr_ctx_destroy(ctx); +} + +// ============================================================================ +// Test: memkit_xdl_open_from_phdr with NULL info +// ============================================================================ + +TEST(memkit_xdl_open_from_phdr_null_info) { + void* handle = memkit_xdl_open_from_phdr(NULL); + assert(handle == NULL); +} + +// ============================================================================ +// Test: Library iteration finds libc.so +// ============================================================================ + +TEST(memkit_xdl_iterate_finds_libc) { + bool found = false; + int count = memkit_xdl_iterate(find_libc_callback, &found, XDL_DEFAULT); + assert(count >= 0); // Should find at least some libraries + assert(found == true); +} + +// ============================================================================ +// Test: Resolve symbol from libc.so +// ============================================================================ + +TEST(memkit_xdl_sym_resolves_open) { + void* handle = memkit_xdl_open("libc.so", XDL_DEFAULT); + if (handle) { + void* sym = memkit_xdl_sym(handle, "open", NULL); + assert(sym != NULL); + memkit_xdl_close(handle); + } +} + +// ============================================================================ +// Test: Resolve symbol with size output +// ============================================================================ + +TEST(memkit_xdl_sym_with_size) { + void* handle = memkit_xdl_open("libc.so", XDL_DEFAULT); + if (handle) { + size_t size = 0; + void* sym = memkit_xdl_sym(handle, "open", &size); + assert(sym != NULL); + // Size may be 0 for some symbols, but should not crash + memkit_xdl_close(handle); + } +} + +// ============================================================================ +// Test: Get library info for libc.so +// ============================================================================ + +TEST(memkit_xdl_get_lib_info_libc) { + void* handle = memkit_xdl_open("libc.so", XDL_DEFAULT); + if (handle) { + MemKitLibInfo info; + bool result = memkit_xdl_get_lib_info(handle, &info); + assert(result == true); + assert(info.name != NULL); + assert(info.base != 0); + memkit_xdl_close(handle); + } +} + +// ============================================================================ +// Test: Address-to-symbol with main() address +// ============================================================================ + +TEST(memkit_xdl_addr_to_symbol_main) { + memkit_addr_ctx_t* ctx = memkit_xdl_addr_ctx_create(); + MemKitSymInfo info; + + // Try to resolve the address of this function + bool result = memkit_xdl_addr_to_symbol((void*)&test_memkit_xdl_addr_to_symbol_main, &info, ctx); + assert(result == true); + assert(info.lib_name != NULL); + assert(info.lib_base != 0); + + memkit_xdl_addr_ctx_destroy(ctx); +} + +// ============================================================================ +// Test: XDL_RESOLVE macro +// ============================================================================ + +TEST(xdl_resolve_macro) { + void* sym = XDL_RESOLVE("libc.so", "open"); + assert(sym != NULL); +} + +// ============================================================================ +// Test: XDL_RESOLVE_SIZE macro +// ============================================================================ + +TEST(xdl_resolve_size_macro) { + size_t size; + void* sym = XDL_RESOLVE_SIZE("libc.so", "open", &size); + assert(sym != NULL); +} + +// ============================================================================ +// Main Test Runner +// ============================================================================ + +int main(void) { + printf("===========================================\n"); + printf(" Android-Mem-Kit: xDL Wrapper Unit Tests\n"); + printf("===========================================\n\n"); + + // NULL/Invalid Input Tests + RUN_TEST(memkit_xdl_iterate_null_callback); + RUN_TEST(memkit_xdl_iterate_invalid_flags); + RUN_TEST(memkit_xdl_open_null_name); + RUN_TEST(memkit_xdl_close_null_handle); + RUN_TEST(memkit_xdl_sym_null_handle); + RUN_TEST(memkit_xdl_sym_null_symbol); + RUN_TEST(memkit_xdl_dsym_null_handle); + RUN_TEST(memkit_xdl_dsym_null_symbol); + RUN_TEST(memkit_xdl_get_lib_info_null_handle); + RUN_TEST(memkit_xdl_get_lib_info_null_output); + RUN_TEST(memkit_xdl_addr_ctx_lifecycle); + RUN_TEST(memkit_xdl_addr_to_symbol_null_addr); + RUN_TEST(memkit_xdl_addr_to_symbol_null_output); + RUN_TEST(memkit_xdl_addr_to_symbol4_null_addr); + RUN_TEST(memkit_xdl_addr_to_symbol4_null_output); + RUN_TEST(memkit_xdl_open_from_phdr_null_info); + + // Functional Tests + RUN_TEST(memkit_xdl_iterate_finds_libc); + RUN_TEST(memkit_xdl_sym_resolves_open); + RUN_TEST(memkit_xdl_sym_with_size); + RUN_TEST(memkit_xdl_get_lib_info_libc); + RUN_TEST(memkit_xdl_addr_to_symbol_main); + + // Macro Tests + RUN_TEST(xdl_resolve_macro); + RUN_TEST(xdl_resolve_size_macro); + + // Summary + printf("\n===========================================\n"); + printf(" Test Summary\n"); + printf("===========================================\n"); + printf(" Run: %d\n", g_tests_run); + printf(" Passed: %d\n", g_tests_passed); + printf(" Failed: %d\n", g_tests_failed); + printf("===========================================\n"); + + return (g_tests_failed == 0) ? 0 : 1; +}