* [Qemu-devel] [PATCH v2] Use the GDB JIT debugging interface
@ 2012-03-19 19:25 Richard Henderson
2012-03-19 19:25 ` [Qemu-devel] [PATCH] tcg: " Richard Henderson
2012-03-19 20:08 ` [Qemu-devel] [PATCH v2] " Peter Maydell
0 siblings, 2 replies; 6+ messages in thread
From: Richard Henderson @ 2012-03-19 19:25 UTC (permalink / raw)
To: qemu-devel
I was reminded about this from a comment on the list, where
a new developer had gotten confused by our backtrace.
Changes from V1 to V2:
* Rebase to master.
* Do not try to allocate the ELF stuff within the code_gen_buffer.
Instead we can use a SHT_NOBITS trick to say that's out-of-line.
* Use C structures (with alignment and packing annotations) to create
the Dwarf2 unwind info. Previously I had used inline assembly.
Example:
Breakpoint 1, helper_idivl_EAX (t0=512)
at /home/rth/work/qemu/qemu/target-i386/op_helper.c:1882
1882 {
(gdb) where
#0 helper_idivl_EAX (t0=512)
at /home/rth/work/qemu/qemu/target-i386/op_helper.c:1882
#1 0x00005555557c0fdf in static_code_gen_buffer ()
#2 0x000055555559714d in cpu_x86_exec (env=0x5555577fba30)
at /home/rth/work/qemu/qemu/cpu-exec.c:564
#3 0x00005555555b3290 in cpu_loop (env=0x5555577fba30)
at /home/rth/work/qemu/qemu/linux-user/main.c:317
#4 0x00005555555b40a4 in main (argc=<optimized out>, argv=<optimized out>,
envp=<optimized out>) at /home/rth/work/qemu/qemu/linux-user/main.c:3826
r~
Richard Henderson (1):
tcg: Use the GDB JIT debugging interface.
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(-)
--
1.7.7.6
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Qemu-devel] [PATCH] tcg: Use the GDB JIT debugging interface.
2012-03-19 19:25 [Qemu-devel] [PATCH v2] Use the GDB JIT debugging interface Richard Henderson
@ 2012-03-19 19:25 ` Richard Henderson
2012-03-24 16:11 ` Blue Swirl
2012-03-19 20:08 ` [Qemu-devel] [PATCH v2] " Peter Maydell
1 sibling, 1 reply; 6+ messages in thread
From: Richard Henderson @ 2012-03-19 19:25 UTC (permalink / raw)
To: qemu-devel
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 <rth@twiddle.net>
---
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);
--
1.7.7.6
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] [PATCH v2] Use the GDB JIT debugging interface
2012-03-19 19:25 [Qemu-devel] [PATCH v2] Use the GDB JIT debugging interface Richard Henderson
2012-03-19 19:25 ` [Qemu-devel] [PATCH] tcg: " Richard Henderson
@ 2012-03-19 20:08 ` Peter Maydell
2012-03-19 20:48 ` Richard Henderson
1 sibling, 1 reply; 6+ messages in thread
From: Peter Maydell @ 2012-03-19 20:08 UTC (permalink / raw)
To: Richard Henderson; +Cc: qemu-devel
On 19 March 2012 19:25, Richard Henderson <rth@twiddle.net> wrote:
> I was reminded about this from a comment on the list, where
> a new developer had gotten confused by our backtrace.
This is neat.
> Example:
>
> Breakpoint 1, helper_idivl_EAX (t0=512)
> at /home/rth/work/qemu/qemu/target-i386/op_helper.c:1882
> 1882 {
> (gdb) where
> #0 helper_idivl_EAX (t0=512)
> at /home/rth/work/qemu/qemu/target-i386/op_helper.c:1882
> #1 0x00005555557c0fdf in static_code_gen_buffer ()
> #2 0x000055555559714d in cpu_x86_exec (env=0x5555577fba30)
> at /home/rth/work/qemu/qemu/cpu-exec.c:564
> #3 0x00005555555b3290 in cpu_loop (env=0x5555577fba30)
> at /home/rth/work/qemu/qemu/linux-user/main.c:317
> #4 0x00005555555b40a4 in main (argc=<optimized out>, argv=<optimized out>,
> envp=<optimized out>) at /home/rth/work/qemu/qemu/linux-user/main.c:3826
In system mode I get a backtrace like this:
(gdb) bt
#0 helper_set_cp15 (env=0x80b41500, insn=3993505559, val=0) at
/home/pm215/src/qemu/qemu/target-arm/helper.c:1455
#1 0x0101983d in ?? ()
#2 0x8020bec3 in cpu_arm_exec (env=0x80b41500) at
/home/pm215/src/qemu/qemu/cpu-exec.c:564
#3 0x8020e978 in tcg_cpu_exec (env=0x80b41500) at
/home/pm215/src/qemu/qemu/cpus.c:1022
#4 0x8020eab8 in tcg_exec_all () at /home/pm215/src/qemu/qemu/cpus.c:1054
#5 0x8020df8f in qemu_tcg_cpu_thread_fn (arg=0x80b41500) at
/home/pm215/src/qemu/qemu/cpus.c:772
#6 0x003bcd31 in start_thread (arg=0xaeecdb70) at pthread_create.c:304
#7 0x0089d46e in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:130
Backtrace stopped: Not enough registers or memory available to unwind further
...is it possible to put in a useful string to replace that '??'
in line #1?
Any chance of some comments describing (a) what the limited debug
info does and what this does/doesn't support? [for instance it
doesn't seem to support doing a gdb backtrace while you're inside
the code_gen_prologue code] and (b) what the cpu-specific tcg backend
code needs to do/provide to support this?
thanks
-- PMM
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] [PATCH v2] Use the GDB JIT debugging interface
2012-03-19 20:08 ` [Qemu-devel] [PATCH v2] " Peter Maydell
@ 2012-03-19 20:48 ` Richard Henderson
0 siblings, 0 replies; 6+ messages in thread
From: Richard Henderson @ 2012-03-19 20:48 UTC (permalink / raw)
To: Peter Maydell; +Cc: qemu-devel
On 03/19/12 13:08, Peter Maydell wrote:
> In system mode I get a backtrace like this:
>
> (gdb) bt
> #0 helper_set_cp15 (env=0x80b41500, insn=3993505559, val=0) at
> /home/pm215/src/qemu/qemu/target-arm/helper.c:1455
> #1 0x0101983d in ?? ()
>
> ...is it possible to put in a useful string to replace that '??'
> in line #1?
I'm not sure. Maybe, with some more effort.
GDB seems to be ignoring the elf-level symbol table, but reading
the dwarf info. So it would be possible to add a minimal debug_info
section as well. "Minimal" is irritatingly large though...
> Any chance of some comments describing (a) what the limited debug
> info does and what this does/doesn't support? [for instance it
> doesn't seem to support doing a gdb backtrace while you're inside
> the code_gen_prologue code] and (b) what the cpu-specific tcg backend
> code needs to do/provide to support this?
Putting this in tcg.c, perhaps? Sure.
You're right that I don't try to handle code_gen_prologue.
Getting that right is more difficult. Of course, I doubt
we need to auto-generate the prologue code *at all*. Some
statically generated assembler would do just as well, at
which point we *could* add .cfi directives to handle that code.
All the cpu-specific tcg backend needs to do is provide the
contents of a .debug_frame section that describes the steady-
state frame established by the prologue. Since the entire
code_gen_buffer uses this same frame, the unwind is fixed for
the entire buffer.
r~
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] [PATCH] tcg: Use the GDB JIT debugging interface.
2012-03-19 19:25 ` [Qemu-devel] [PATCH] tcg: " Richard Henderson
@ 2012-03-24 16:11 ` Blue Swirl
2012-03-24 17:04 ` Peter Maydell
0 siblings, 1 reply; 6+ messages in thread
From: Blue Swirl @ 2012-03-24 16:11 UTC (permalink / raw)
To: Richard Henderson; +Cc: qemu-devel
Thanks, applied.
On Mon, Mar 19, 2012 at 19:25, Richard Henderson <rth@twiddle.net> wrote:
> 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 <rth@twiddle.net>
> ---
> 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);
> --
> 1.7.7.6
>
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] [PATCH] tcg: Use the GDB JIT debugging interface.
2012-03-24 16:11 ` Blue Swirl
@ 2012-03-24 17:04 ` Peter Maydell
0 siblings, 0 replies; 6+ messages in thread
From: Peter Maydell @ 2012-03-24 17:04 UTC (permalink / raw)
To: Blue Swirl; +Cc: qemu-devel, Richard Henderson
Er, this is the v2 patch, which Richard updated based on review
comments, can you apply the later v3 one instead?
thanks
-- PMM
On 24 March 2012 16:11, Blue Swirl <blauwirbel@gmail.com> wrote:
> Thanks, applied.
>
> On Mon, Mar 19, 2012 at 19:25, Richard Henderson <rth@twiddle.net> wrote:
>> 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 <rth@twiddle.net>
>> ---
>> 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);
>> --
>> 1.7.7.6
>>
>>
>
--
12345678901234567890123456789012345678901234567890123456789012345678901234567890
1 2 3 4 5 6 7 8
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2012-03-24 17:04 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-03-19 19:25 [Qemu-devel] [PATCH v2] Use the GDB JIT debugging interface Richard Henderson
2012-03-19 19:25 ` [Qemu-devel] [PATCH] tcg: " Richard Henderson
2012-03-24 16:11 ` Blue Swirl
2012-03-24 17:04 ` Peter Maydell
2012-03-19 20:08 ` [Qemu-devel] [PATCH v2] " Peter Maydell
2012-03-19 20:48 ` Richard Henderson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).