tcg: Use the GDB JIT debugging interface.
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> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
This commit is contained in:
parent
08c4ea294f
commit
813da6277c
1
elf.h
1
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)
|
||||
|
1
exec.c
1
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
|
||||
|
@ -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));
|
||||
}
|
||||
|
194
tcg/tcg.c
194
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user