diff options
| author | Yang Liu <liuyang22@iscas.ac.cn> | 2024-12-18 05:01:33 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-17 22:01:33 +0100 |
| commit | 6e0f32f4ca4b35bda036caa6ad3abcae4ae64cdc (patch) | |
| tree | 39a144624b528deba4421d7b71098db69d99eb80 | |
| parent | 62eca11beef3f211a93235b18d556332fafc74de (diff) | |
| download | box64-6e0f32f4ca4b35bda036caa6ad3abcae4ae64cdc.tar.gz box64-6e0f32f4ca4b35bda036caa6ad3abcae4ae64cdc.zip | |
Added initial GDBJIT support (#2162)
* Added GDBJIT support * fix * fix
| -rw-r--r-- | CMakeLists.txt | 19 | ||||
| -rw-r--r-- | gdbjit/reader.c | 41 | ||||
| -rw-r--r-- | src/core.c | 1 | ||||
| -rw-r--r-- | src/dynarec/arm64/dynarec_arm64_functions.c | 6 | ||||
| -rw-r--r-- | src/dynarec/arm64/dynarec_arm64_private.h | 1 | ||||
| -rw-r--r-- | src/dynarec/dynablock_private.h | 2 | ||||
| -rw-r--r-- | src/dynarec/dynarec_native.c | 11 | ||||
| -rw-r--r-- | src/dynarec/la64/dynarec_la64_functions.c | 4 | ||||
| -rw-r--r-- | src/dynarec/la64/dynarec_la64_private.h | 1 | ||||
| -rw-r--r-- | src/dynarec/rv64/dynarec_rv64_functions.c | 4 | ||||
| -rw-r--r-- | src/dynarec/rv64/dynarec_rv64_private.h | 1 | ||||
| -rw-r--r-- | src/include/debug.h | 1 | ||||
| -rw-r--r-- | src/include/gdbjit.h | 32 | ||||
| -rw-r--r-- | src/tools/gdbjit.c | 134 |
14 files changed, 257 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6903beb8..766f531c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,7 @@ option(WITH_MOLD "Set to ON to use with mold" ${WITH_MOLD}) option(BOX32 "Set to ON to add Linux 32bits support (experimental, do not use)" ${BOX32}) option(BOX32_BINFMT "Also setup binfmt integration for box32" ${BOX32_BINFMT}) option(LARCH64_ABI_1 "Set to ON if building for Loongarch64 ABI 1.0 system" ${LARCH64_ABI_1}) +option(GDBJIT "Set to ON to enable GDB JIT support" ${GDBJIT}) if(TERMUX) set(TERMUX_PATH "/data/data/com.termux/files") @@ -95,6 +96,11 @@ if(ANDROID) set(BAD_SIGNAL ON CACHE BOOL "") endif() +if(GDBJIT) + message(STATUS "Build with GDBJIT support") + add_definitions(-DGDBJIT) +endif() + option(LD80BITS "Set to ON if host device have 80bits long double (i.e. i386)" ${LD80BITS}) option(NOALIGN "Set to ON if host device doesn't need re-align (i.e. i386)" ${NOALIGN}) option(ARM_DYNAREC "Set to ON to use ARM Dynamic Recompilation" ${ARM_DYNAREC}) @@ -386,6 +392,7 @@ set(ELFLOADER_SRC "${BOX64_ROOT}/src/tools/box64stack.c" "${BOX64_ROOT}/src/tools/bridge.c" "${BOX64_ROOT}/src/tools/callback.c" + "${BOX64_ROOT}/src/tools/gdbjit.c" "${BOX64_ROOT}/src/tools/my_cpuid.c" "${BOX64_ROOT}/src/tools/fileutils.c" "${BOX64_ROOT}/src/tools/pathcoll.c" @@ -1039,6 +1046,11 @@ if(LARCH64_DYNAREC) ) endif() + +if(GDBJIT) +set(GDBJITREADER "${BOX64_ROOT}/gdbjit/reader.c") +endif() + if(DYNAREC) set(DYNAREC_PASS "${BOX64_ROOT}/src/wrapped/generated/wrapper.h" @@ -1082,6 +1094,10 @@ if(DYNAREC) $<TARGET_OBJECTS:native_pass3> $<TARGET_OBJECTS:test_interpreter> ) + + if(GDBJIT) + add_library(box64gdbjitreader SHARED ${GDBJITREADER}) + endif() endif() # creates git_head.h @@ -1171,6 +1187,9 @@ if(NOT _x86 AND NOT _x86_64) install(PROGRAMS ${CMAKE_SOURCE_DIR}/tests/box64-bash DESTINATION ${TERMUX_PATH}/usr/bin) endif() endif() + if (GDBJIT) + install(TARGETS box64gdbjitreader RUNTIME DESTINATION lib) + endif() if(NOT NO_CONF_INSTALL) configure_file(system/box64.conf.cmake system/box64.conf) if(NOT TERMUX) diff --git a/gdbjit/reader.c b/gdbjit/reader.c new file mode 100644 index 00000000..024e88ea --- /dev/null +++ b/gdbjit/reader.c @@ -0,0 +1,41 @@ +#include "gdbjit.h" + +static enum gdb_status read_debug_info(struct gdb_reader_funcs* self, struct gdb_symbol_callbacks* cbs, void* memory, long memory_sz) +{ + gdbjit_block_t* block = (gdbjit_block_t*)memory; + + struct gdb_object* object = cbs->object_open(cbs); + struct gdb_symtab* symtab = cbs->symtab_open(cbs, object, block->filename); + cbs->block_open(cbs, symtab, NULL, block->start, block->end, "(block)"); + + cbs->line_mapping_add(cbs, symtab, block->nlines, block->lines); + + cbs->symtab_close(cbs, symtab); + cbs->object_close(cbs, object); + + return GDB_SUCCESS; +} + +enum gdb_status unwind_frame(struct gdb_reader_funcs* self, struct gdb_unwind_callbacks* cbs) +{ + return GDB_SUCCESS; +} + +struct gdb_frame_id get_frame_id(struct gdb_reader_funcs* self, struct gdb_unwind_callbacks* cbs) +{ + struct gdb_frame_id frame = {0xdeadbeef, 0}; + return frame; +} + +void destroy_reader(struct gdb_reader_funcs* self) {} + +__attribute__((visibility("default"))) struct gdb_reader_funcs* gdb_init_reader(void) +{ + static struct gdb_reader_funcs funcs = {GDB_READER_INTERFACE_VERSION, NULL, read_debug_info, unwind_frame, get_frame_id, destroy_reader}; + return &funcs; +} + +__attribute__((visibility("default"))) int plugin_is_GPL_compatible (void) +{ + return 0; +} \ No newline at end of file diff --git a/src/core.c b/src/core.c index 3cc5caec..ddb83ce2 100644 --- a/src/core.c +++ b/src/core.c @@ -72,6 +72,7 @@ int box64_rdtsc_1ghz = 0; uint8_t box64_rdtsc_shift = 0; char* box64_insert_args = NULL; char* box64_new_args = NULL; +int box64_dynarec_gdbjit = 1; #ifdef DYNAREC int box64_dynarec = 1; int box64_dynarec_dump = 0; diff --git a/src/dynarec/arm64/dynarec_arm64_functions.c b/src/dynarec/arm64/dynarec_arm64_functions.c index 590000bf..e473e6ca 100644 --- a/src/dynarec/arm64/dynarec_arm64_functions.c +++ b/src/dynarec/arm64/dynarec_arm64_functions.c @@ -25,6 +25,7 @@ #include "dynarec_arm64_functions.h" #include "custommem.h" #include "bridge.h" +#include "gdbjit.h" // Get a FPU scratch reg int fpu_get_scratch(dynarec_arm_t* dyn, int ninst) @@ -748,6 +749,11 @@ void inst_name_pass3(dynarec_native_t* dyn, int ninst, const char* name, rex_t r dynarec_log(LOG_NONE, " %s:%d/%d", dyn->insts[ninst].n.swapped?"SWP":"CMB", dyn->insts[ninst].n.combined1, dyn->insts[ninst].n.combined2); dynarec_log(LOG_NONE, "%s\n", (box64_dynarec_dump>1)?"\e[m":""); } + if (box64_dynarec_gdbjit) { + zydis_dec_t* dec = rex.is32bits?my_context->dec32:my_context->dec; + const char *inst_name = dec ? DecodeX64Trace(dec, dyn->insts[ninst].x64.addr) : name; + dyn->gdbjit_block = GdbJITBlockAddLine(dyn->gdbjit_block, (dyn->native_start + dyn->insts[ninst].address), inst_name); + } } void print_opcode(dynarec_native_t* dyn, int ninst, uint32_t opcode) diff --git a/src/dynarec/arm64/dynarec_arm64_private.h b/src/dynarec/arm64/dynarec_arm64_private.h index 113e4341..8f10b0f5 100644 --- a/src/dynarec/arm64/dynarec_arm64_private.h +++ b/src/dynarec/arm64/dynarec_arm64_private.h @@ -164,6 +164,7 @@ typedef struct dynarec_arm_s { uint8_t doublepop; uint8_t always_test; uint8_t abort; // abort the creation of the block + void* gdbjit_block; } dynarec_arm_t; void add_next(dynarec_arm_t *dyn, uintptr_t addr); diff --git a/src/dynarec/dynablock_private.h b/src/dynarec/dynablock_private.h index 4bf7dd7a..a152bd4e 100644 --- a/src/dynarec/dynablock_private.h +++ b/src/dynarec/dynablock_private.h @@ -24,4 +24,4 @@ typedef struct dynablock_s { void* jmpnext; // a branch jmpnext code when block is marked } dynablock_t; -#endif //__DYNABLOCK_PRIVATE_H_ \ No newline at end of file +#endif //__DYNABLOCK_PRIVATE_H_ diff --git a/src/dynarec/dynarec_native.c b/src/dynarec/dynarec_native.c index ece7baa0..011815bc 100644 --- a/src/dynarec/dynarec_native.c +++ b/src/dynarec/dynarec_native.c @@ -1,3 +1,4 @@ +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> @@ -22,6 +23,7 @@ #include "dynarec_native.h" #include "dynarec_arch.h" #include "dynarec_next.h" +#include "gdbjit.h" void printf_x64_instruction(zydis_dec_t* dec, instruction_x64_t* inst, const char* name) { uint8_t *ip = (uint8_t*)inst->addr; @@ -574,6 +576,9 @@ void* FillBlock64(dynablock_t* block, uintptr_t addr, int alternate, int is32bit protectDB(addr, 1); // init the helper dynarec_native_t helper = {0}; +#ifdef GDBJIT + helper.gdbjit_block = box_calloc(1, sizeof(gdbjit_block_t)); +#endif current_helper = &helper; helper.dynablock = block; helper.start = addr; @@ -748,6 +753,9 @@ void* FillBlock64(dynablock_t* block, uintptr_t addr, int alternate, int is32bit printFunctionAddr(helper.start, " => "); dynarec_log(LOG_NONE, "%s\n", (box64_dynarec_dump>1)?"\e[m":""); } + if (box64_dynarec_gdbjit) { + GdbJITNewBlock(helper.gdbjit_block, (GDB_CORE_ADDR)block->actual_block, (GDB_CORE_ADDR)block->actual_block + native_size); + } int oldtable64size = helper.table64size; size_t oldnativesize = helper.native_size; size_t oldinstsize = helper.insts_size; @@ -782,6 +790,9 @@ void* FillBlock64(dynablock_t* block, uintptr_t addr, int alternate, int is32bit //block->x64_addr = (void*)start; block->x64_size = end-start; // all done... + if (box64_dynarec_gdbjit) { + GdbJITBlockReady(helper.gdbjit_block); + } __clear_cache(actual_p, actual_p+sz); // need to clear the cache before execution... block->hash = X31_hash_code(block->x64_addr, block->x64_size); // Check if something changed, to abort if it is diff --git a/src/dynarec/la64/dynarec_la64_functions.c b/src/dynarec/la64/dynarec_la64_functions.c index 925113f0..bdb60094 100644 --- a/src/dynarec/la64/dynarec_la64_functions.c +++ b/src/dynarec/la64/dynarec_la64_functions.c @@ -25,6 +25,7 @@ #include "dynarec_la64_functions.h" #include "custommem.h" #include "bridge.h" +#include "gdbjit.h" #define XMM0 0 #define XMM8 16 @@ -306,6 +307,9 @@ void inst_name_pass3(dynarec_native_t* dyn, int ninst, const char* name, rex_t r dynarec_log(LOG_NONE, " %s:%d/%d", dyn->insts[ninst].lsx.swapped ? "SWP" : "CMB", dyn->insts[ninst].lsx.combined1, dyn->insts[ninst].lsx.combined2); dynarec_log(LOG_NONE, "%s\n", (box64_dynarec_dump > 1) ? "\e[m" : ""); } + if (box64_dynarec_gdbjit) { + dyn->gdbjit_block = GdbJITBlockAddLine(dyn->gdbjit_block, dyn->insts[ninst].address, name); + } } // will go badly if address is unaligned diff --git a/src/dynarec/la64/dynarec_la64_private.h b/src/dynarec/la64/dynarec_la64_private.h index a9578fc6..4f57d366 100644 --- a/src/dynarec/la64/dynarec_la64_private.h +++ b/src/dynarec/la64/dynarec_la64_private.h @@ -133,6 +133,7 @@ typedef struct dynarec_la64_s { uint8_t smwrite; // for strongmem model emulation uint8_t always_test; uint8_t abort; + void* gdbjit_block; } dynarec_la64_t; void add_next(dynarec_la64_t *dyn, uintptr_t addr); diff --git a/src/dynarec/rv64/dynarec_rv64_functions.c b/src/dynarec/rv64/dynarec_rv64_functions.c index 3930d6ea..e14be4f2 100644 --- a/src/dynarec/rv64/dynarec_rv64_functions.c +++ b/src/dynarec/rv64/dynarec_rv64_functions.c @@ -27,6 +27,7 @@ #include "custommem.h" #include "bridge.h" #include "rv64_lock.h" +#include "gdbjit.h" #define XMM0 0 #define X870 XMM0 + 16 @@ -722,6 +723,9 @@ void inst_name_pass3(dynarec_native_t* dyn, int ninst, const char* name, rex_t r dynarec_log(LOG_NONE, " %s:%d/%d", dyn->insts[ninst].e.swapped ? "SWP" : "CMB", dyn->insts[ninst].e.combined1, dyn->insts[ninst].e.combined2); dynarec_log(LOG_NONE, "%s\n", (box64_dynarec_dump > 1) ? "\e[m" : ""); } + if (box64_dynarec_gdbjit) { + dyn->gdbjit_block = GdbJITBlockAddLine(dyn->gdbjit_block, dyn->insts[ninst].address, name); + } } void print_opcode(dynarec_native_t* dyn, int ninst, uint32_t opcode) diff --git a/src/dynarec/rv64/dynarec_rv64_private.h b/src/dynarec/rv64/dynarec_rv64_private.h index 9e876fd3..aecea9e9 100644 --- a/src/dynarec/rv64/dynarec_rv64_private.h +++ b/src/dynarec/rv64/dynarec_rv64_private.h @@ -180,6 +180,7 @@ typedef struct dynarec_rv64_s { uint8_t inst_sew; // sew inside current instruction, for vsetvli elimination uint8_t inst_vl; // vl inside current instruction, for vsetvli elimination uint8_t inst_vlmul; // vlmul inside current instruction + void* gdbjit_block; } dynarec_rv64_t; // v0 is hardware wired to vector mask register, which should be always reserved diff --git a/src/include/debug.h b/src/include/debug.h index cbaab9db..0a8d4058 100644 --- a/src/include/debug.h +++ b/src/include/debug.h @@ -18,6 +18,7 @@ extern int box64_rdtsc_1ghz; extern uint8_t box64_rdtsc_shift; extern int box64_is32bits; extern int box64_x11sync; +extern int box64_dynarec_gdbjit; #ifdef DYNAREC extern int box64_dynarec_dump; extern int box64_dynarec_trace; diff --git a/src/include/gdbjit.h b/src/include/gdbjit.h new file mode 100644 index 00000000..70405d3e --- /dev/null +++ b/src/include/gdbjit.h @@ -0,0 +1,32 @@ +#ifndef __GDBJIT_H__ +#define __GDBJIT_H__ + +#if defined(DYNAREC) && defined(GDBJIT) +#include <gdb/jit-reader.h> +#include <stdio.h> +#include <stdint.h> + +typedef struct gdbjit_block_s { + char filename[32]; + FILE* file; + GDB_CORE_ADDR start; + GDB_CORE_ADDR end; + uintptr_t alloced; + size_t nlines; + struct gdb_line_mapping lines[0]; +} gdbjit_block_t; + + +void GdbJITNewBlock(gdbjit_block_t* block, GDB_CORE_ADDR start, GDB_CORE_ADDR end); +gdbjit_block_t* GdbJITBlockAddLine(gdbjit_block_t* block, GDB_CORE_ADDR addr, const char* line); +void GdbJITBlockReady(gdbjit_block_t* block); + +#else + +#define GdbJITNewBlock(a, b, c) +#define GdbJITBlockAddLine(a, b, c) NULL +#define GdbJITBlockReady(a) + +#endif + +#endif // __GDBJIT_H__ diff --git a/src/tools/gdbjit.c b/src/tools/gdbjit.c new file mode 100644 index 00000000..d91c4c05 --- /dev/null +++ b/src/tools/gdbjit.c @@ -0,0 +1,134 @@ +#ifdef GDBJIT +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <elf.h> +#include <errno.h> +#include "gdbjit.h" +#include "dynablock.h" +#include "debug.h" + + +/* GDB JIT Compilation Interface ----------------------------------------------- + * https://sourceware.org/gdb/current/onlinedocs/gdb.html/JIT-Interface.html + */ + +enum { + GDBJIT_NOACTION = 0, + GDBJIT_REGISTER, + GDBJIT_UNREGISTER +}; + +typedef struct gdbjit_code_entry_s { + struct gdbjit_code_entry_s* next_entry; + struct gdbjit_code_entry_s* prev_entry; + const char* symfile_addr; + uint64_t symfile_size; +} gdbjit_code_entry_t; + +typedef struct gdbjit_descriptor_s { + uint32_t version; + uint32_t action_flag; + gdbjit_code_entry_t* relevant_entry; + gdbjit_code_entry_t* first_entry; +} gdbjit_descriptor_t; + +/* GDB puts a breakpoint in this function. This can't be optimized out. */ +void __attribute__((noinline)) __jit_debug_register_code() +{ + asm volatile("" ::: "memory"); +}; + +/* Make sure to specify the version statically, because the debugger may check + * the version before we can set it. + */ +gdbjit_descriptor_t __jit_debug_descriptor = { 1, GDBJIT_NOACTION, NULL, NULL }; + +/* --------------------------------------------------------------------------- */ + +void GdbJITNewBlock(gdbjit_block_t* block, GDB_CORE_ADDR start, GDB_CORE_ADDR end) +{ + if (!block) + return; + + memset(block, 0, sizeof(gdbjit_block_t)); + + strcpy(block->filename, "/tmp/box64gdbjit-XXXXXX.S"); + int fd = mkstemps(block->filename, 2); + block->file = fdopen(fd, "w"); + + block->start = start; + block->end = end; + block->alloced = block->nlines = 0; +} + +static size_t GdbJITLinesAvailable(gdbjit_block_t* block) +{ + if (!block) + return 0; + + return block->alloced > block->nlines; +} + +#define GDBJIT_LINES_MAX_PREALLOC 64 + +static gdbjit_block_t* GdbJITMakeRoom(gdbjit_block_t* block) +{ + if (!block) return NULL; + + if (!GdbJITLinesAvailable(block)) { + size_t new_size = block->alloced + GDBJIT_LINES_MAX_PREALLOC; + block = box_realloc(block, sizeof(gdbjit_block_t) + new_size * sizeof(struct gdb_line_mapping)); + if (!block) return NULL; + block->alloced = new_size; + } + return block; +} + +gdbjit_block_t* GdbJITBlockAddLine(gdbjit_block_t* block, GDB_CORE_ADDR addr, const char* line) +{ + block->nlines++; + block = GdbJITMakeRoom(block); + if (!block) return NULL; + + block->lines[block->nlines-1].pc = addr; + block->lines[block->nlines-1].line = block->nlines; + fprintf(block->file, "%s\n", line); + fflush(block->file); + return block; +} + +void GdbJITBlockReady(gdbjit_block_t* block) +{ + if (!block) return; + + if (block->nlines == 0) { + fclose(block->file); + box_free(block); + return; + } + + gdbjit_code_entry_t* entry = (gdbjit_code_entry_t*)box_malloc(sizeof(gdbjit_code_entry_t)); + if (!entry) { + fclose(block->file); + box_free(block); + return; + } + + fclose(block->file); + + entry->symfile_addr = (const char*)block; + entry->symfile_size = sizeof(gdbjit_block_t) + block->nlines * sizeof(struct gdb_line_mapping); + + if (__jit_debug_descriptor.first_entry) { + __jit_debug_descriptor.relevant_entry->next_entry = entry; + entry->prev_entry = __jit_debug_descriptor.relevant_entry; + } else { + __jit_debug_descriptor.first_entry = entry; + } + + __jit_debug_descriptor.relevant_entry = entry; + __jit_debug_descriptor.action_flag = GDBJIT_REGISTER; + __jit_debug_register_code(); +} +#endif |