From patchwork Mon Mar 19 19:25:11 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Henderson X-Patchwork-Id: 147613 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id D96AFB700D for ; Tue, 20 Mar 2012 06:25:39 +1100 (EST) Received: from localhost ([::1]:48578 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S9iD3-0006LA-P5 for incoming@patchwork.ozlabs.org; Mon, 19 Mar 2012 15:25:37 -0400 Received: from eggs.gnu.org ([208.118.235.92]:40433) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S9iCv-0006KP-K0 for qemu-devel@nongnu.org; Mon, 19 Mar 2012 15:25:31 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1S9iCp-0001FO-LH for qemu-devel@nongnu.org; Mon, 19 Mar 2012 15:25:29 -0400 Received: from mail-pz0-f45.google.com ([209.85.210.45]:44784) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S9iCp-0001F6-8v for qemu-devel@nongnu.org; Mon, 19 Mar 2012 15:25:23 -0400 Received: by dadp14 with SMTP id p14so11473515dad.4 for ; Mon, 19 Mar 2012 12:25:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:subject:date:message-id:x-mailer:in-reply-to :references; bh=bTc/3FZmyy6f5/pNEB1Xow8rtjVPfpZQ2XGdOZ9jR6U=; b=LRp+1UDM/OLhmq1CUFn2dYka8FBr3lQZY2ayRXO/V5TlxxM1pshp6/+q71OgiZ2E/E gKTgGvCYgxhZZXUdAFKMSXg4FBuR8HZH2T7eeXADQ6sIlUnNAk4ImmEWLSAmFI9swcss WCzFhT8m2m7NgENQFqcaxd3SjTioYcPiRBLcREZce+CCLwHmzm2qiY5RLCfLky76r5v0 3mUEpZMNJt+5OfZ6G1eRs0tdlTX82cZR32hAAUbzbEaXuE3u7pqj0tFCWGf6gMULzXrI ODWAcTVD8QctgIdNYqSzAmWvOAesZbKm4W941A2clt+lhkY3I5E8KG3mbK95yEPhq3+3 tSQQ== Received: by 10.68.222.227 with SMTP id qp3mr36944506pbc.137.1332185120918; Mon, 19 Mar 2012 12:25:20 -0700 (PDT) Received: from anchor.twiddle.home.com ([173.160.232.49]) by mx.google.com with ESMTPS id l1sm11960837pbs.34.2012.03.19.12.25.19 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 19 Mar 2012 12:25:20 -0700 (PDT) From: Richard Henderson To: qemu-devel@nongnu.org Date: Mon, 19 Mar 2012 12:25:11 -0700 Message-Id: <1332185111-20739-2-git-send-email-rth@twiddle.net> X-Mailer: git-send-email 1.7.7.6 In-Reply-To: <1332185111-20739-1-git-send-email-rth@twiddle.net> References: <1332185111-20739-1-git-send-email-rth@twiddle.net> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.85.210.45 Subject: [Qemu-devel] [PATCH] tcg: Use the GDB JIT debugging interface. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This allows us to generate unwind info for the dynamicly generated code in the code_gen_buffer. Only i386 is converted at this point. Signed-off-by: Richard Henderson --- elf.h | 1 + exec.c | 1 + tcg/i386/tcg-target.c | 114 ++++++++++++++++++++++++++-- tcg/tcg.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++ tcg/tcg.h | 2 + 5 files changed, 303 insertions(+), 9 deletions(-) diff --git a/elf.h b/elf.h index 2e05d34..310e05a 100644 --- a/elf.h +++ b/elf.h @@ -216,6 +216,7 @@ typedef int64_t Elf64_Sxword; #define ELF_ST_BIND(x) ((x) >> 4) #define ELF_ST_TYPE(x) (((unsigned int) x) & 0xf) +#define ELF_ST_INFO(bind, type) (((bind) << 4) | ((type) & 0xf)) #define ELF32_ST_BIND(x) ELF_ST_BIND(x) #define ELF32_ST_TYPE(x) ELF_ST_TYPE(x) #define ELF64_ST_BIND(x) ELF_ST_BIND(x) diff --git a/exec.c b/exec.c index be392e2..dd24939 100644 --- a/exec.c +++ b/exec.c @@ -636,6 +636,7 @@ void tcg_exec_init(unsigned long tb_size) cpu_gen_init(); code_gen_alloc(tb_size); code_gen_ptr = code_gen_buffer; + tcg_register_jit(code_gen_buffer, code_gen_buffer_size); page_init(); #if !defined(CONFIG_USER_ONLY) || !defined(CONFIG_USE_GUEST_BASE) /* There's no guest base to take into account, so go ahead and diff --git a/tcg/i386/tcg-target.c b/tcg/i386/tcg-target.c index 43a51a1..871a7e7 100644 --- a/tcg/i386/tcg-target.c +++ b/tcg/i386/tcg-target.c @@ -1989,22 +1989,29 @@ static int tcg_target_callee_save_regs[] = { #endif }; +/* Compute frame size via macros, to share between tcg_target_qemu_prologue + and tcg_register_jit. */ + +#define PUSH_SIZE \ + ((1 + ARRAY_SIZE(tcg_target_callee_save_regs)) \ + * (TCG_TARGET_REG_BITS / 8)) + +#define FRAME_SIZE \ + ((PUSH_SIZE \ + + TCG_STATIC_CALL_ARGS_SIZE \ + + CPU_TEMP_BUF_NLONGS * sizeof(long) \ + + TCG_TARGET_STACK_ALIGN - 1) \ + & ~(TCG_TARGET_STACK_ALIGN - 1)) + /* Generate global QEMU prologue and epilogue code */ static void tcg_target_qemu_prologue(TCGContext *s) { - int i, frame_size, push_size, stack_addend; + int i, stack_addend; /* TB prologue */ /* Reserve some stack space, also for TCG temps. */ - push_size = 1 + ARRAY_SIZE(tcg_target_callee_save_regs); - push_size *= TCG_TARGET_REG_BITS / 8; - - frame_size = push_size + TCG_STATIC_CALL_ARGS_SIZE + - CPU_TEMP_BUF_NLONGS * sizeof(long); - frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & - ~(TCG_TARGET_STACK_ALIGN - 1); - stack_addend = frame_size - push_size; + stack_addend = FRAME_SIZE - PUSH_SIZE; tcg_set_frame(s, TCG_REG_CALL_STACK, TCG_STATIC_CALL_ARGS_SIZE, CPU_TEMP_BUF_NLONGS * sizeof(long)); @@ -2070,3 +2077,92 @@ static void tcg_target_init(TCGContext *s) tcg_add_target_add_op_defs(x86_op_defs); } + +typedef struct { + uint32_t len __attribute__((aligned((sizeof(void *))))); + uint32_t id; + uint8_t version; + char augmentation[1]; + uint8_t code_align; + uint8_t data_align; + uint8_t return_column; +} DebugFrameCIE; + +typedef struct { + uint32_t len __attribute__((aligned((sizeof(void *))))); + uint32_t cie_offset; + tcg_target_long func_start __attribute__((packed)); + tcg_target_long func_len __attribute__((packed)); + uint8_t def_cfa[4]; + uint8_t reg_ofs[14]; +} DebugFrameFDE; + +typedef struct { + DebugFrameCIE cie; + DebugFrameFDE fde; +} DebugFrame; + +#if TCG_TARGET_REG_BITS == 64 +#define ELF_HOST_MACHINE EM_X86_64 +static DebugFrame debug_frame = { + .cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .cie.id = -1, + .cie.version = 1, + .cie.code_align = 1, + .cie.data_align = 0x78, /* sleb128 -8 */ + .cie.return_column = 16, + + .fde.len = sizeof(DebugFrameFDE)-4, /* length after .len member */ + .fde.def_cfa = { + 12, 7, /* DW_CFA_def_cfa %rsp, ... */ + (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ + (FRAME_SIZE >> 7) + }, + .fde.reg_ofs = { + 0x90, 1, /* DW_CFA_offset, %rip, -8 */ + /* The following ordering must match tcg_target_callee_save_regs. */ + 0x86, 2, /* DW_CFA_offset, %rbp, -16 */ + 0x83, 3, /* DW_CFA_offset, %rbx, -24 */ + 0x8c, 4, /* DW_CFA_offset, %r12, -32 */ + 0x8d, 5, /* DW_CFA_offset, %r13, -40 */ + 0x8e, 6, /* DW_CFA_offset, %r14, -48 */ + 0x8f, 7, /* DW_CFA_offset, %r15, -56 */ + } +}; +#else +#define ELF_HOST_MACHINE EM_386 +static DebugFrame debug_frame = { + .cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .cie.id = -1, + .cie.version = 1, + .cie.code_align = 1, + .cie.data_align = 0x7c, /* sleb128 -4 */ + .cie.return_column = 8, + + .fde.len = sizeof(DebugFrameFDE)-4, /* length after .len member */ + .fde.def_cfa = { + 12, 4, /* DW_CFA_def_cfa %esp, ... */ + (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ + (FRAME_SIZE >> 7) + }, + .fde.reg_ofs = { + 0x88, 1, /* DW_CFA_offset, %eip, -4 */ + /* The following ordering must match tcg_target_callee_save_regs. */ + 0x85, 2, /* DW_CFA_offset, %ebp, -8 */ + 0x83, 3, /* DW_CFA_offset, %ebx, -12 */ + 0x86, 4, /* DW_CFA_offset, %esi, -16 */ + 0x87, 5, /* DW_CFA_offset, %edi, -20 */ + } +}; +#endif + +void tcg_register_jit(void *buf, size_t buf_size) +{ + /* We're expecting a 2 byte uleb128 encoded value. */ + assert(FRAME_SIZE >> 14 == 0); + + debug_frame.fde.func_start = (tcg_target_long) buf; + debug_frame.fde.func_len = buf_size; + + tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); +} diff --git a/tcg/tcg.c b/tcg/tcg.c index ccfcd1a..eb595ce 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -28,6 +28,9 @@ #include "config.h" +/* Define to jump the ELF file used to communicate with GDB. */ +#undef DEBUG_JIT + #if !defined(CONFIG_DEBUG_TCG) && !defined(NDEBUG) /* define it to suppress various consistency checks (faster) */ #define NDEBUG @@ -45,6 +48,18 @@ #include "cpu.h" #include "tcg-op.h" + +#if TCG_TARGET_REG_BITS == 64 +# define ELF_CLASS ELFCLASS64 +#else +# define ELF_CLASS ELFCLASS32 +#endif +#ifdef HOST_WORDS_BIGENDIAN +# define ELF_DATA ELFDATA2MSB +#else +# define ELF_DATA ELFDATA2LSB +#endif + #include "elf.h" #if defined(CONFIG_USE_GUEST_BASE) && !defined(TCG_TARGET_HAS_GUEST_BASE) @@ -57,6 +72,10 @@ static void tcg_target_qemu_prologue(TCGContext *s); static void patch_reloc(uint8_t *code_ptr, int type, tcg_target_long value, tcg_target_long addend); +static void tcg_register_jit_int(void *buf, size_t size, + void *debug_frame, size_t debug_frame_size) + __attribute__((unused)); + /* Forward declarations for functions declared and used in tcg-target.c. */ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str); static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, @@ -2231,3 +2250,178 @@ void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf) cpu_fprintf(f, "[TCG profiler not compiled]\n"); } #endif + +#ifdef ELF_HOST_MACHINE +/* The backend should define ELF_HOST_MACHINE to indicate both what value to + put into the ELF image and to indicate support for the feature. */ + +/* Begin GDB interface. THE FOLLOWING MUST MATCH GDB DOCS. */ +typedef enum { + JIT_NOACTION = 0, + JIT_REGISTER_FN, + JIT_UNREGISTER_FN +} jit_actions_t; + +struct jit_code_entry { + struct jit_code_entry *next_entry; + struct jit_code_entry *prev_entry; + const void *symfile_addr; + uint64_t symfile_size; +}; + +struct jit_descriptor { + uint32_t version; + uint32_t action_flag; + struct jit_code_entry *relevant_entry; + struct jit_code_entry *first_entry; +}; + +void __jit_debug_register_code(void) __attribute__((noinline)); +void __jit_debug_register_code(void) +{ + asm(""); +} + +/* Must statically initialize the version, because GDB may check + the version before we can set it. */ +struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 }; + +/* End GDB interface. */ + +static int find_string(const char *strtab, const char *str) +{ + const char *p = strtab + 1; + + while (1) { + if (strcmp(p, str) == 0) { + return p - strtab; + } + p += strlen(p) + 1; + } +} + +static void tcg_register_jit_int(void *buf, size_t buf_size, + void *debug_frame, size_t debug_frame_size) +{ + static const char strings[64] = + "\0" + ".text\0" + ".debug_frame\0" + ".symtab\0" + ".strtab\0" + "code_gen_buffer"; + + struct ElfImage { + ElfW(Ehdr) ehdr; + ElfW(Phdr) phdr; + ElfW(Shdr) shdr[5]; + ElfW(Sym) sym[1]; + char str[64]; + }; + + /* We only need a single jit entry; statically allocate it. */ + static struct jit_code_entry one_entry; + + size_t img_size = sizeof(struct ElfImage) + debug_frame_size; + struct ElfImage *img = g_malloc0(img_size); + + img->ehdr.e_ident[EI_MAG0] = ELFMAG0; + img->ehdr.e_ident[EI_MAG1] = ELFMAG1; + img->ehdr.e_ident[EI_MAG2] = ELFMAG2; + img->ehdr.e_ident[EI_MAG3] = ELFMAG3; + img->ehdr.e_ident[EI_CLASS] = ELF_CLASS; + img->ehdr.e_ident[EI_DATA] = ELF_DATA; + img->ehdr.e_ident[EI_VERSION] = EV_CURRENT; + img->ehdr.e_type = ET_EXEC; + img->ehdr.e_machine = ELF_HOST_MACHINE; + img->ehdr.e_version = EV_CURRENT; + img->ehdr.e_phoff = offsetof(struct ElfImage, phdr); + img->ehdr.e_shoff = offsetof(struct ElfImage, shdr); + img->ehdr.e_ehsize = sizeof(ElfW(Shdr)); + img->ehdr.e_phentsize = sizeof(ElfW(Phdr)); + img->ehdr.e_phnum = 1; + img->ehdr.e_shentsize = sizeof(img->shdr[0]); + img->ehdr.e_shnum = ARRAY_SIZE(img->shdr); + img->ehdr.e_shstrndx = ARRAY_SIZE(img->shdr) - 1; + + img->phdr.p_type = PT_LOAD; + img->phdr.p_offset = (char *)buf - (char *)img; + img->phdr.p_vaddr = (ElfW(Addr))buf; + img->phdr.p_paddr = img->phdr.p_vaddr; + img->phdr.p_filesz = 0; + img->phdr.p_memsz = buf_size; + img->phdr.p_flags = PF_X; + + memcpy(img->str, strings, sizeof(img->str)); + + img->shdr[0].sh_type = SHT_NULL; + + /* Trick: The contents of code_gen_buffer are not present in this fake + ELF file; that got allocated elsewhere, discontiguously. Therefore + we mark .text as SHT_NOBITS (similar to .bss) so that readers will + not look for contents. We can record any address at will. */ + img->shdr[1].sh_name = find_string(img->str, ".text"); + img->shdr[1].sh_type = SHT_NOBITS; + img->shdr[1].sh_flags = SHF_EXECINSTR | SHF_ALLOC; + img->shdr[1].sh_addr = (ElfW(Addr))buf; + img->shdr[1].sh_size = buf_size; + + img->shdr[2].sh_name = find_string(img->str, ".debug_frame"); + img->shdr[2].sh_type = SHT_PROGBITS; + img->shdr[2].sh_offset = sizeof(*img); + img->shdr[2].sh_size = debug_frame_size; + memcpy(img + 1, debug_frame, debug_frame_size); + + img->shdr[3].sh_name = find_string(img->str, ".symtab"); + img->shdr[3].sh_type = SHT_SYMTAB; + img->shdr[3].sh_offset = offsetof(struct ElfImage, sym); + img->shdr[3].sh_size = sizeof(img->sym); + img->shdr[3].sh_info = ARRAY_SIZE(img->sym); + img->shdr[3].sh_link = img->ehdr.e_shstrndx; + img->shdr[3].sh_entsize = sizeof(ElfW(Sym)); + + img->shdr[4].sh_name = find_string(img->str, ".strtab"); + img->shdr[4].sh_type = SHT_STRTAB; + img->shdr[4].sh_offset = offsetof(struct ElfImage, str); + img->shdr[4].sh_size = sizeof(img->str); + + img->sym[0].st_name = find_string(img->str, "code_gen_buffer"); + img->sym[0].st_info = ELF_ST_INFO(STB_GLOBAL, STT_FUNC); + img->sym[0].st_shndx = 1; + img->sym[0].st_value = (ElfW(Addr))buf; + img->sym[0].st_size = buf_size; + +#ifdef DEBUG_JIT + /* Enable this block to be able to debug the ELF image file creation. + One can use readelf, objdump, or other inspection utilities. */ + { + FILE *f = fopen("/tmp/qemu.jit", "w+b"); + if (f) { + if (fwrite(img, img_size, 1, f) != buf_size) { + /* Avoid stupid unused return value warning for fwrite. */ + } + fclose(f); + } + } +#endif + + one_entry.symfile_addr = img; + one_entry.symfile_size = img_size; + + __jit_debug_descriptor.action_flag = JIT_REGISTER_FN; + __jit_debug_descriptor.relevant_entry = &one_entry; + __jit_debug_descriptor.first_entry = &one_entry; + __jit_debug_register_code(); +} +#else +/* No support for the feature. Provide the entry point expected by exec.c. */ + +static void tcg_register_jit_int(void *buf, size_t size, + void *debug_frame, size_t debug_frame_size) +{ +} + +void tcg_register_jit(void *buf, size_t buf_size) +{ +} +#endif /* ELF_HOST_MACHINE */ diff --git a/tcg/tcg.h b/tcg/tcg.h index 5f6c647..a83bddd 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -586,3 +586,5 @@ extern uint8_t code_gen_prologue[]; # define tcg_qemu_tb_exec(env, tb_ptr) \ ((tcg_target_ulong (*)(void *, void *))code_gen_prologue)(env, tb_ptr) #endif + +void tcg_register_jit(void *buf, size_t buf_size);