| /* |
| ** Client for the GDB JIT API. |
| ** Copyright (C) 2005-2015 Mike Pall. See Copyright Notice in luajit.h |
| */ |
| |
| #define lj_gdbjit_c |
| #define LUA_CORE |
| |
| #include "lj_obj.h" |
| |
| #if LJ_HASJIT |
| |
| #include "lj_gc.h" |
| #include "lj_err.h" |
| #include "lj_debug.h" |
| #include "lj_frame.h" |
| #include "lj_jit.h" |
| #include "lj_dispatch.h" |
| |
| /* This is not compiled in by default. |
| ** Enable with -DLUAJIT_USE_GDBJIT in the Makefile and recompile everything. |
| */ |
| #ifdef LUAJIT_USE_GDBJIT |
| |
| /* The GDB JIT API allows JIT compilers to pass debug information about |
| ** JIT-compiled code back to GDB. You need at least GDB 7.0 or higher |
| ** to see it in action. |
| ** |
| ** This is a passive API, so it works even when not running under GDB |
| ** or when attaching to an already running process. Alas, this implies |
| ** enabling it always has a non-negligible overhead -- do not use in |
| ** release mode! |
| ** |
| ** The LuaJIT GDB JIT client is rather minimal at the moment. It gives |
| ** each trace a symbol name and adds a source location and frame unwind |
| ** information. Obviously LuaJIT itself and any embedding C application |
| ** should be compiled with debug symbols, too (see the Makefile). |
| ** |
| ** Traces are named TRACE_1, TRACE_2, ... these correspond to the trace |
| ** numbers from -jv or -jdump. Use "break TRACE_1" or "tbreak TRACE_1" etc. |
| ** to set breakpoints on specific traces (even ahead of their creation). |
| ** |
| ** The source location for each trace allows listing the corresponding |
| ** source lines with the GDB command "list" (but only if the Lua source |
| ** has been loaded from a file). Currently this is always set to the |
| ** location where the trace has been started. |
| ** |
| ** Frame unwind information can be inspected with the GDB command |
| ** "info frame". This also allows proper backtraces across JIT-compiled |
| ** code with the GDB command "bt". |
| ** |
| ** You probably want to add the following settings to a .gdbinit file |
| ** (or add them to ~/.gdbinit): |
| ** set disassembly-flavor intel |
| ** set breakpoint pending on |
| ** |
| ** Here's a sample GDB session: |
| ** ------------------------------------------------------------------------ |
| |
| $ cat >x.lua |
| for outer=1,100 do |
| for inner=1,100 do end |
| end |
| ^D |
| |
| $ luajit -jv x.lua |
| [TRACE 1 x.lua:2] |
| [TRACE 2 (1/3) x.lua:1 -> 1] |
| |
| $ gdb --quiet --args luajit x.lua |
| (gdb) tbreak TRACE_1 |
| Function "TRACE_1" not defined. |
| Temporary breakpoint 1 (TRACE_1) pending. |
| (gdb) run |
| Starting program: luajit x.lua |
| |
| Temporary breakpoint 1, TRACE_1 () at x.lua:2 |
| 2 for inner=1,100 do end |
| (gdb) list |
| 1 for outer=1,100 do |
| 2 for inner=1,100 do end |
| 3 end |
| (gdb) bt |
| #0 TRACE_1 () at x.lua:2 |
| #1 0x08053690 in lua_pcall [...] |
| [...] |
| #7 0x0806ff90 in main [...] |
| (gdb) disass TRACE_1 |
| Dump of assembler code for function TRACE_1: |
| 0xf7fd9fba <TRACE_1+0>: mov DWORD PTR ds:0xf7e0e2a0,0x1 |
| 0xf7fd9fc4 <TRACE_1+10>: movsd xmm7,QWORD PTR [edx+0x20] |
| [...] |
| 0xf7fd9ff8 <TRACE_1+62>: jmp 0xf7fd2014 |
| End of assembler dump. |
| (gdb) tbreak TRACE_2 |
| Function "TRACE_2" not defined. |
| Temporary breakpoint 2 (TRACE_2) pending. |
| (gdb) cont |
| Continuing. |
| |
| Temporary breakpoint 2, TRACE_2 () at x.lua:1 |
| 1 for outer=1,100 do |
| (gdb) info frame |
| Stack level 0, frame at 0xffffd7c0: |
| eip = 0xf7fd9f60 in TRACE_2 (x.lua:1); saved eip 0x8053690 |
| called by frame at 0xffffd7e0 |
| source language unknown. |
| Arglist at 0xffffd78c, args: |
| Locals at 0xffffd78c, Previous frame's sp is 0xffffd7c0 |
| Saved registers: |
| ebx at 0xffffd7ac, ebp at 0xffffd7b8, esi at 0xffffd7b0, edi at 0xffffd7b4, |
| eip at 0xffffd7bc |
| (gdb) |
| |
| ** ------------------------------------------------------------------------ |
| */ |
| |
| /* -- GDB JIT API --------------------------------------------------------- */ |
| |
| /* GDB JIT actions. */ |
| enum { |
| GDBJIT_NOACTION = 0, |
| GDBJIT_REGISTER, |
| GDBJIT_UNREGISTER |
| }; |
| |
| /* GDB JIT entry. */ |
| typedef struct GDBJITentry { |
| struct GDBJITentry *next_entry; |
| struct GDBJITentry *prev_entry; |
| const char *symfile_addr; |
| uint64_t symfile_size; |
| } GDBJITentry; |
| |
| /* GDB JIT descriptor. */ |
| typedef struct GDBJITdesc { |
| uint32_t version; |
| uint32_t action_flag; |
| GDBJITentry *relevant_entry; |
| GDBJITentry *first_entry; |
| } GDBJITdesc; |
| |
| GDBJITdesc __jit_debug_descriptor = { |
| 1, GDBJIT_NOACTION, NULL, NULL |
| }; |
| |
| /* GDB sets a breakpoint at this function. */ |
| void LJ_NOINLINE __jit_debug_register_code() |
| { |
| __asm__ __volatile__(""); |
| }; |
| |
| /* -- In-memory ELF object definitions ------------------------------------ */ |
| |
| /* ELF definitions. */ |
| typedef struct ELFheader { |
| uint8_t emagic[4]; |
| uint8_t eclass; |
| uint8_t eendian; |
| uint8_t eversion; |
| uint8_t eosabi; |
| uint8_t eabiversion; |
| uint8_t epad[7]; |
| uint16_t type; |
| uint16_t machine; |
| uint32_t version; |
| uintptr_t entry; |
| uintptr_t phofs; |
| uintptr_t shofs; |
| uint32_t flags; |
| uint16_t ehsize; |
| uint16_t phentsize; |
| uint16_t phnum; |
| uint16_t shentsize; |
| uint16_t shnum; |
| uint16_t shstridx; |
| } ELFheader; |
| |
| typedef struct ELFsectheader { |
| uint32_t name; |
| uint32_t type; |
| uintptr_t flags; |
| uintptr_t addr; |
| uintptr_t ofs; |
| uintptr_t size; |
| uint32_t link; |
| uint32_t info; |
| uintptr_t align; |
| uintptr_t entsize; |
| } ELFsectheader; |
| |
| #define ELFSECT_IDX_ABS 0xfff1 |
| |
| enum { |
| ELFSECT_TYPE_PROGBITS = 1, |
| ELFSECT_TYPE_SYMTAB = 2, |
| ELFSECT_TYPE_STRTAB = 3, |
| ELFSECT_TYPE_NOBITS = 8 |
| }; |
| |
| #define ELFSECT_FLAGS_WRITE 1 |
| #define ELFSECT_FLAGS_ALLOC 2 |
| #define ELFSECT_FLAGS_EXEC 4 |
| |
| typedef struct ELFsymbol { |
| #if LJ_64 |
| uint32_t name; |
| uint8_t info; |
| uint8_t other; |
| uint16_t sectidx; |
| uintptr_t value; |
| uint64_t size; |
| #else |
| uint32_t name; |
| uintptr_t value; |
| uint32_t size; |
| uint8_t info; |
| uint8_t other; |
| uint16_t sectidx; |
| #endif |
| } ELFsymbol; |
| |
| enum { |
| ELFSYM_TYPE_FUNC = 2, |
| ELFSYM_TYPE_FILE = 4, |
| ELFSYM_BIND_LOCAL = 0 << 4, |
| ELFSYM_BIND_GLOBAL = 1 << 4, |
| }; |
| |
| /* DWARF definitions. */ |
| #define DW_CIE_VERSION 1 |
| |
| enum { |
| DW_CFA_nop = 0x0, |
| DW_CFA_offset_extended = 0x5, |
| DW_CFA_def_cfa = 0xc, |
| DW_CFA_def_cfa_offset = 0xe, |
| DW_CFA_offset_extended_sf = 0x11, |
| DW_CFA_advance_loc = 0x40, |
| DW_CFA_offset = 0x80 |
| }; |
| |
| enum { |
| DW_EH_PE_udata4 = 3, |
| DW_EH_PE_textrel = 0x20 |
| }; |
| |
| enum { |
| DW_TAG_compile_unit = 0x11 |
| }; |
| |
| enum { |
| DW_children_no = 0, |
| DW_children_yes = 1 |
| }; |
| |
| enum { |
| DW_AT_name = 0x03, |
| DW_AT_stmt_list = 0x10, |
| DW_AT_low_pc = 0x11, |
| DW_AT_high_pc = 0x12 |
| }; |
| |
| enum { |
| DW_FORM_addr = 0x01, |
| DW_FORM_data4 = 0x06, |
| DW_FORM_string = 0x08 |
| }; |
| |
| enum { |
| DW_LNS_extended_op = 0, |
| DW_LNS_copy = 1, |
| DW_LNS_advance_pc = 2, |
| DW_LNS_advance_line = 3 |
| }; |
| |
| enum { |
| DW_LNE_end_sequence = 1, |
| DW_LNE_set_address = 2 |
| }; |
| |
| enum { |
| #if LJ_TARGET_X86 |
| DW_REG_AX, DW_REG_CX, DW_REG_DX, DW_REG_BX, |
| DW_REG_SP, DW_REG_BP, DW_REG_SI, DW_REG_DI, |
| DW_REG_RA, |
| #elif LJ_TARGET_X64 |
| /* Yes, the order is strange, but correct. */ |
| DW_REG_AX, DW_REG_DX, DW_REG_CX, DW_REG_BX, |
| DW_REG_SI, DW_REG_DI, DW_REG_BP, DW_REG_SP, |
| DW_REG_8, DW_REG_9, DW_REG_10, DW_REG_11, |
| DW_REG_12, DW_REG_13, DW_REG_14, DW_REG_15, |
| DW_REG_RA, |
| #elif LJ_TARGET_ARM |
| DW_REG_SP = 13, |
| DW_REG_RA = 14, |
| #elif LJ_TARGET_PPC |
| DW_REG_SP = 1, |
| DW_REG_RA = 65, |
| DW_REG_CR = 70, |
| #elif LJ_TARGET_MIPS |
| DW_REG_SP = 29, |
| DW_REG_RA = 31, |
| #else |
| #error "Unsupported target architecture" |
| #endif |
| }; |
| |
| /* Minimal list of sections for the in-memory ELF object. */ |
| enum { |
| GDBJIT_SECT_NULL, |
| GDBJIT_SECT_text, |
| GDBJIT_SECT_eh_frame, |
| GDBJIT_SECT_shstrtab, |
| GDBJIT_SECT_strtab, |
| GDBJIT_SECT_symtab, |
| GDBJIT_SECT_debug_info, |
| GDBJIT_SECT_debug_abbrev, |
| GDBJIT_SECT_debug_line, |
| GDBJIT_SECT__MAX |
| }; |
| |
| enum { |
| GDBJIT_SYM_UNDEF, |
| GDBJIT_SYM_FILE, |
| GDBJIT_SYM_FUNC, |
| GDBJIT_SYM__MAX |
| }; |
| |
| /* In-memory ELF object. */ |
| typedef struct GDBJITobj { |
| ELFheader hdr; /* ELF header. */ |
| ELFsectheader sect[GDBJIT_SECT__MAX]; /* ELF sections. */ |
| ELFsymbol sym[GDBJIT_SYM__MAX]; /* ELF symbol table. */ |
| uint8_t space[4096]; /* Space for various section data. */ |
| } GDBJITobj; |
| |
| /* Combined structure for GDB JIT entry and ELF object. */ |
| typedef struct GDBJITentryobj { |
| GDBJITentry entry; |
| size_t sz; |
| GDBJITobj obj; |
| } GDBJITentryobj; |
| |
| /* Template for in-memory ELF header. */ |
| static const ELFheader elfhdr_template = { |
| .emagic = { 0x7f, 'E', 'L', 'F' }, |
| .eclass = LJ_64 ? 2 : 1, |
| .eendian = LJ_ENDIAN_SELECT(1, 2), |
| .eversion = 1, |
| #if LJ_TARGET_LINUX |
| .eosabi = 0, /* Nope, it's not 3. */ |
| #elif defined(__FreeBSD__) |
| .eosabi = 9, |
| #elif defined(__NetBSD__) |
| .eosabi = 2, |
| #elif defined(__OpenBSD__) |
| .eosabi = 12, |
| #elif defined(__DragonFly__) |
| .eosabi = 0, |
| #elif (defined(__sun__) && defined(__svr4__)) |
| .eosabi = 6, |
| #else |
| .eosabi = 0, |
| #endif |
| .eabiversion = 0, |
| .epad = { 0, 0, 0, 0, 0, 0, 0 }, |
| .type = 1, |
| #if LJ_TARGET_X86 |
| .machine = 3, |
| #elif LJ_TARGET_X64 |
| .machine = 62, |
| #elif LJ_TARGET_ARM |
| .machine = 40, |
| #elif LJ_TARGET_PPC |
| .machine = 20, |
| #elif LJ_TARGET_MIPS |
| .machine = 8, |
| #else |
| #error "Unsupported target architecture" |
| #endif |
| .version = 1, |
| .entry = 0, |
| .phofs = 0, |
| .shofs = offsetof(GDBJITobj, sect), |
| .flags = 0, |
| .ehsize = sizeof(ELFheader), |
| .phentsize = 0, |
| .phnum = 0, |
| .shentsize = sizeof(ELFsectheader), |
| .shnum = GDBJIT_SECT__MAX, |
| .shstridx = GDBJIT_SECT_shstrtab |
| }; |
| |
| /* -- In-memory ELF object generation ------------------------------------- */ |
| |
| /* Context for generating the ELF object for the GDB JIT API. */ |
| typedef struct GDBJITctx { |
| uint8_t *p; /* Pointer to next address in obj.space. */ |
| uint8_t *startp; /* Pointer to start address in obj.space. */ |
| GCtrace *T; /* Generate symbols for this trace. */ |
| uintptr_t mcaddr; /* Machine code address. */ |
| MSize szmcode; /* Size of machine code. */ |
| MSize spadjp; /* Stack adjustment for parent trace or interpreter. */ |
| MSize spadj; /* Stack adjustment for trace itself. */ |
| BCLine lineno; /* Starting line number. */ |
| const char *filename; /* Starting file name. */ |
| size_t objsize; /* Final size of ELF object. */ |
| GDBJITobj obj; /* In-memory ELF object. */ |
| } GDBJITctx; |
| |
| /* Add a zero-terminated string. */ |
| static uint32_t gdbjit_strz(GDBJITctx *ctx, const char *str) |
| { |
| uint8_t *p = ctx->p; |
| uint32_t ofs = (uint32_t)(p - ctx->startp); |
| do { |
| *p++ = (uint8_t)*str; |
| } while (*str++); |
| ctx->p = p; |
| return ofs; |
| } |
| |
| /* Append a decimal number. */ |
| static void gdbjit_catnum(GDBJITctx *ctx, uint32_t n) |
| { |
| if (n >= 10) { uint32_t m = n / 10; n = n % 10; gdbjit_catnum(ctx, m); } |
| *ctx->p++ = '0' + n; |
| } |
| |
| /* Add a ULEB128 value. */ |
| static void gdbjit_uleb128(GDBJITctx *ctx, uint32_t v) |
| { |
| uint8_t *p = ctx->p; |
| for (; v >= 0x80; v >>= 7) |
| *p++ = (uint8_t)((v & 0x7f) | 0x80); |
| *p++ = (uint8_t)v; |
| ctx->p = p; |
| } |
| |
| /* Add a SLEB128 value. */ |
| static void gdbjit_sleb128(GDBJITctx *ctx, int32_t v) |
| { |
| uint8_t *p = ctx->p; |
| for (; (uint32_t)(v+0x40) >= 0x80; v >>= 7) |
| *p++ = (uint8_t)((v & 0x7f) | 0x80); |
| *p++ = (uint8_t)(v & 0x7f); |
| ctx->p = p; |
| } |
| |
| /* Shortcuts to generate DWARF structures. */ |
| #define DB(x) (*p++ = (x)) |
| #define DI8(x) (*(int8_t *)p = (x), p++) |
| #define DU16(x) (*(uint16_t *)p = (x), p += 2) |
| #define DU32(x) (*(uint32_t *)p = (x), p += 4) |
| #define DADDR(x) (*(uintptr_t *)p = (x), p += sizeof(uintptr_t)) |
| #define DUV(x) (ctx->p = p, gdbjit_uleb128(ctx, (x)), p = ctx->p) |
| #define DSV(x) (ctx->p = p, gdbjit_sleb128(ctx, (x)), p = ctx->p) |
| #define DSTR(str) (ctx->p = p, gdbjit_strz(ctx, (str)), p = ctx->p) |
| #define DALIGNNOP(s) while ((uintptr_t)p & ((s)-1)) *p++ = DW_CFA_nop |
| #define DSECT(name, stmt) \ |
| { uint32_t *szp_##name = (uint32_t *)p; p += 4; stmt \ |
| *szp_##name = (uint32_t)((p-(uint8_t *)szp_##name)-4); } \ |
| |
| /* Initialize ELF section headers. */ |
| static void LJ_FASTCALL gdbjit_secthdr(GDBJITctx *ctx) |
| { |
| ELFsectheader *sect; |
| |
| *ctx->p++ = '\0'; /* Empty string at start of string table. */ |
| |
| #define SECTDEF(id, tp, al) \ |
| sect = &ctx->obj.sect[GDBJIT_SECT_##id]; \ |
| sect->name = gdbjit_strz(ctx, "." #id); \ |
| sect->type = ELFSECT_TYPE_##tp; \ |
| sect->align = (al) |
| |
| SECTDEF(text, NOBITS, 16); |
| sect->flags = ELFSECT_FLAGS_ALLOC|ELFSECT_FLAGS_EXEC; |
| sect->addr = ctx->mcaddr; |
| sect->ofs = 0; |
| sect->size = ctx->szmcode; |
| |
| SECTDEF(eh_frame, PROGBITS, sizeof(uintptr_t)); |
| sect->flags = ELFSECT_FLAGS_ALLOC; |
| |
| SECTDEF(shstrtab, STRTAB, 1); |
| SECTDEF(strtab, STRTAB, 1); |
| |
| SECTDEF(symtab, SYMTAB, sizeof(uintptr_t)); |
| sect->ofs = offsetof(GDBJITobj, sym); |
| sect->size = sizeof(ctx->obj.sym); |
| sect->link = GDBJIT_SECT_strtab; |
| sect->entsize = sizeof(ELFsymbol); |
| sect->info = GDBJIT_SYM_FUNC; |
| |
| SECTDEF(debug_info, PROGBITS, 1); |
| SECTDEF(debug_abbrev, PROGBITS, 1); |
| SECTDEF(debug_line, PROGBITS, 1); |
| |
| #undef SECTDEF |
| } |
| |
| /* Initialize symbol table. */ |
| static void LJ_FASTCALL gdbjit_symtab(GDBJITctx *ctx) |
| { |
| ELFsymbol *sym; |
| |
| *ctx->p++ = '\0'; /* Empty string at start of string table. */ |
| |
| sym = &ctx->obj.sym[GDBJIT_SYM_FILE]; |
| sym->name = gdbjit_strz(ctx, "JIT mcode"); |
| sym->sectidx = ELFSECT_IDX_ABS; |
| sym->info = ELFSYM_TYPE_FILE|ELFSYM_BIND_LOCAL; |
| |
| sym = &ctx->obj.sym[GDBJIT_SYM_FUNC]; |
| sym->name = gdbjit_strz(ctx, "TRACE_"); ctx->p--; |
| gdbjit_catnum(ctx, ctx->T->traceno); *ctx->p++ = '\0'; |
| sym->sectidx = GDBJIT_SECT_text; |
| sym->value = 0; |
| sym->size = ctx->szmcode; |
| sym->info = ELFSYM_TYPE_FUNC|ELFSYM_BIND_GLOBAL; |
| } |
| |
| /* Initialize .eh_frame section. */ |
| static void LJ_FASTCALL gdbjit_ehframe(GDBJITctx *ctx) |
| { |
| uint8_t *p = ctx->p; |
| uint8_t *framep = p; |
| |
| /* Emit DWARF EH CIE. */ |
| DSECT(CIE, |
| DU32(0); /* Offset to CIE itself. */ |
| DB(DW_CIE_VERSION); |
| DSTR("zR"); /* Augmentation. */ |
| DUV(1); /* Code alignment factor. */ |
| DSV(-(int32_t)sizeof(uintptr_t)); /* Data alignment factor. */ |
| DB(DW_REG_RA); /* Return address register. */ |
| DB(1); DB(DW_EH_PE_textrel|DW_EH_PE_udata4); /* Augmentation data. */ |
| DB(DW_CFA_def_cfa); DUV(DW_REG_SP); DUV(sizeof(uintptr_t)); |
| #if LJ_TARGET_PPC |
| DB(DW_CFA_offset_extended_sf); DB(DW_REG_RA); DSV(-1); |
| #else |
| DB(DW_CFA_offset|DW_REG_RA); DUV(1); |
| #endif |
| DALIGNNOP(sizeof(uintptr_t)); |
| ) |
| |
| /* Emit DWARF EH FDE. */ |
| DSECT(FDE, |
| DU32((uint32_t)(p-framep)); /* Offset to CIE. */ |
| DU32(0); /* Machine code offset relative to .text. */ |
| DU32(ctx->szmcode); /* Machine code length. */ |
| DB(0); /* Augmentation data. */ |
| /* Registers saved in CFRAME. */ |
| #if LJ_TARGET_X86 |
| DB(DW_CFA_offset|DW_REG_BP); DUV(2); |
| DB(DW_CFA_offset|DW_REG_DI); DUV(3); |
| DB(DW_CFA_offset|DW_REG_SI); DUV(4); |
| DB(DW_CFA_offset|DW_REG_BX); DUV(5); |
| #elif LJ_TARGET_X64 |
| DB(DW_CFA_offset|DW_REG_BP); DUV(2); |
| DB(DW_CFA_offset|DW_REG_BX); DUV(3); |
| DB(DW_CFA_offset|DW_REG_15); DUV(4); |
| DB(DW_CFA_offset|DW_REG_14); DUV(5); |
| /* Extra registers saved for JIT-compiled code. */ |
| DB(DW_CFA_offset|DW_REG_13); DUV(9); |
| DB(DW_CFA_offset|DW_REG_12); DUV(10); |
| #elif LJ_TARGET_ARM |
| { |
| int i; |
| for (i = 11; i >= 4; i--) { DB(DW_CFA_offset|i); DUV(2+(11-i)); } |
| } |
| #elif LJ_TARGET_PPC |
| { |
| int i; |
| DB(DW_CFA_offset_extended); DB(DW_REG_CR); DUV(55); |
| for (i = 14; i <= 31; i++) { |
| DB(DW_CFA_offset|i); DUV(37+(31-i)); |
| DB(DW_CFA_offset|32|i); DUV(2+2*(31-i)); |
| } |
| } |
| #elif LJ_TARGET_MIPS |
| { |
| int i; |
| DB(DW_CFA_offset|30); DUV(2); |
| for (i = 23; i >= 16; i--) { DB(DW_CFA_offset|i); DUV(26-i); } |
| for (i = 30; i >= 20; i -= 2) { DB(DW_CFA_offset|32|i); DUV(42-i); } |
| } |
| #else |
| #error "Unsupported target architecture" |
| #endif |
| if (ctx->spadjp != ctx->spadj) { /* Parent/interpreter stack frame size. */ |
| DB(DW_CFA_def_cfa_offset); DUV(ctx->spadjp); |
| DB(DW_CFA_advance_loc|1); /* Only an approximation. */ |
| } |
| DB(DW_CFA_def_cfa_offset); DUV(ctx->spadj); /* Trace stack frame size. */ |
| DALIGNNOP(sizeof(uintptr_t)); |
| ) |
| |
| ctx->p = p; |
| } |
| |
| /* Initialize .debug_info section. */ |
| static void LJ_FASTCALL gdbjit_debuginfo(GDBJITctx *ctx) |
| { |
| uint8_t *p = ctx->p; |
| |
| DSECT(info, |
| DU16(2); /* DWARF version. */ |
| DU32(0); /* Abbrev offset. */ |
| DB(sizeof(uintptr_t)); /* Pointer size. */ |
| |
| DUV(1); /* Abbrev #1: DW_TAG_compile_unit. */ |
| DSTR(ctx->filename); /* DW_AT_name. */ |
| DADDR(ctx->mcaddr); /* DW_AT_low_pc. */ |
| DADDR(ctx->mcaddr + ctx->szmcode); /* DW_AT_high_pc. */ |
| DU32(0); /* DW_AT_stmt_list. */ |
| ) |
| |
| ctx->p = p; |
| } |
| |
| /* Initialize .debug_abbrev section. */ |
| static void LJ_FASTCALL gdbjit_debugabbrev(GDBJITctx *ctx) |
| { |
| uint8_t *p = ctx->p; |
| |
| /* Abbrev #1: DW_TAG_compile_unit. */ |
| DUV(1); DUV(DW_TAG_compile_unit); |
| DB(DW_children_no); |
| DUV(DW_AT_name); DUV(DW_FORM_string); |
| DUV(DW_AT_low_pc); DUV(DW_FORM_addr); |
| DUV(DW_AT_high_pc); DUV(DW_FORM_addr); |
| DUV(DW_AT_stmt_list); DUV(DW_FORM_data4); |
| DB(0); DB(0); |
| |
| ctx->p = p; |
| } |
| |
| #define DLNE(op, s) (DB(DW_LNS_extended_op), DUV(1+(s)), DB((op))) |
| |
| /* Initialize .debug_line section. */ |
| static void LJ_FASTCALL gdbjit_debugline(GDBJITctx *ctx) |
| { |
| uint8_t *p = ctx->p; |
| |
| DSECT(line, |
| DU16(2); /* DWARF version. */ |
| DSECT(header, |
| DB(1); /* Minimum instruction length. */ |
| DB(1); /* is_stmt. */ |
| DI8(0); /* Line base for special opcodes. */ |
| DB(2); /* Line range for special opcodes. */ |
| DB(3+1); /* Opcode base at DW_LNS_advance_line+1. */ |
| DB(0); DB(1); DB(1); /* Standard opcode lengths. */ |
| /* Directory table. */ |
| DB(0); |
| /* File name table. */ |
| DSTR(ctx->filename); DUV(0); DUV(0); DUV(0); |
| DB(0); |
| ) |
| |
| DLNE(DW_LNE_set_address, sizeof(uintptr_t)); DADDR(ctx->mcaddr); |
| if (ctx->lineno) { |
| DB(DW_LNS_advance_line); DSV(ctx->lineno-1); |
| } |
| DB(DW_LNS_copy); |
| DB(DW_LNS_advance_pc); DUV(ctx->szmcode); |
| DLNE(DW_LNE_end_sequence, 0); |
| ) |
| |
| ctx->p = p; |
| } |
| |
| #undef DLNE |
| |
| /* Undef shortcuts. */ |
| #undef DB |
| #undef DI8 |
| #undef DU16 |
| #undef DU32 |
| #undef DADDR |
| #undef DUV |
| #undef DSV |
| #undef DSTR |
| #undef DALIGNNOP |
| #undef DSECT |
| |
| /* Type of a section initializer callback. */ |
| typedef void (LJ_FASTCALL *GDBJITinitf)(GDBJITctx *ctx); |
| |
| /* Call section initializer and set the section offset and size. */ |
| static void gdbjit_initsect(GDBJITctx *ctx, int sect, GDBJITinitf initf) |
| { |
| ctx->startp = ctx->p; |
| ctx->obj.sect[sect].ofs = (uintptr_t)((char *)ctx->p - (char *)&ctx->obj); |
| initf(ctx); |
| ctx->obj.sect[sect].size = (uintptr_t)(ctx->p - ctx->startp); |
| } |
| |
| #define SECTALIGN(p, a) \ |
| ((p) = (uint8_t *)(((uintptr_t)(p) + ((a)-1)) & ~(uintptr_t)((a)-1))) |
| |
| /* Build in-memory ELF object. */ |
| static void gdbjit_buildobj(GDBJITctx *ctx) |
| { |
| GDBJITobj *obj = &ctx->obj; |
| /* Fill in ELF header and clear structures. */ |
| memcpy(&obj->hdr, &elfhdr_template, sizeof(ELFheader)); |
| memset(&obj->sect, 0, sizeof(ELFsectheader)*GDBJIT_SECT__MAX); |
| memset(&obj->sym, 0, sizeof(ELFsymbol)*GDBJIT_SYM__MAX); |
| /* Initialize sections. */ |
| ctx->p = obj->space; |
| gdbjit_initsect(ctx, GDBJIT_SECT_shstrtab, gdbjit_secthdr); |
| gdbjit_initsect(ctx, GDBJIT_SECT_strtab, gdbjit_symtab); |
| gdbjit_initsect(ctx, GDBJIT_SECT_debug_info, gdbjit_debuginfo); |
| gdbjit_initsect(ctx, GDBJIT_SECT_debug_abbrev, gdbjit_debugabbrev); |
| gdbjit_initsect(ctx, GDBJIT_SECT_debug_line, gdbjit_debugline); |
| SECTALIGN(ctx->p, sizeof(uintptr_t)); |
| gdbjit_initsect(ctx, GDBJIT_SECT_eh_frame, gdbjit_ehframe); |
| ctx->objsize = (size_t)((char *)ctx->p - (char *)obj); |
| lua_assert(ctx->objsize < sizeof(GDBJITobj)); |
| } |
| |
| #undef SECTALIGN |
| |
| /* -- Interface to GDB JIT API -------------------------------------------- */ |
| |
| /* Add new entry to GDB JIT symbol chain. */ |
| static void gdbjit_newentry(lua_State *L, GDBJITctx *ctx) |
| { |
| /* Allocate memory for GDB JIT entry and ELF object. */ |
| MSize sz = (MSize)(sizeof(GDBJITentryobj) - sizeof(GDBJITobj) + ctx->objsize); |
| GDBJITentryobj *eo = lj_mem_newt(L, sz, GDBJITentryobj); |
| memcpy(&eo->obj, &ctx->obj, ctx->objsize); /* Copy ELF object. */ |
| eo->sz = sz; |
| ctx->T->gdbjit_entry = (void *)eo; |
| /* Link new entry to chain and register it. */ |
| eo->entry.prev_entry = NULL; |
| eo->entry.next_entry = __jit_debug_descriptor.first_entry; |
| if (eo->entry.next_entry) |
| eo->entry.next_entry->prev_entry = &eo->entry; |
| eo->entry.symfile_addr = (const char *)&eo->obj; |
| eo->entry.symfile_size = ctx->objsize; |
| __jit_debug_descriptor.first_entry = &eo->entry; |
| __jit_debug_descriptor.relevant_entry = &eo->entry; |
| __jit_debug_descriptor.action_flag = GDBJIT_REGISTER; |
| __jit_debug_register_code(); |
| } |
| |
| /* Add debug info for newly compiled trace and notify GDB. */ |
| void lj_gdbjit_addtrace(jit_State *J, GCtrace *T) |
| { |
| GDBJITctx ctx; |
| GCproto *pt = &gcref(T->startpt)->pt; |
| TraceNo parent = T->ir[REF_BASE].op1; |
| const BCIns *startpc = mref(T->startpc, const BCIns); |
| ctx.T = T; |
| ctx.mcaddr = (uintptr_t)T->mcode; |
| ctx.szmcode = T->szmcode; |
| ctx.spadjp = CFRAME_SIZE_JIT + |
| (MSize)(parent ? traceref(J, parent)->spadjust : 0); |
| ctx.spadj = CFRAME_SIZE_JIT + T->spadjust; |
| lua_assert(startpc >= proto_bc(pt) && startpc < proto_bc(pt) + pt->sizebc); |
| ctx.lineno = lj_debug_line(pt, proto_bcpos(pt, startpc)); |
| ctx.filename = proto_chunknamestr(pt); |
| if (*ctx.filename == '@' || *ctx.filename == '=') |
| ctx.filename++; |
| else |
| ctx.filename = "(string)"; |
| gdbjit_buildobj(&ctx); |
| gdbjit_newentry(J->L, &ctx); |
| } |
| |
| /* Delete debug info for trace and notify GDB. */ |
| void lj_gdbjit_deltrace(jit_State *J, GCtrace *T) |
| { |
| GDBJITentryobj *eo = (GDBJITentryobj *)T->gdbjit_entry; |
| if (eo) { |
| if (eo->entry.prev_entry) |
| eo->entry.prev_entry->next_entry = eo->entry.next_entry; |
| else |
| __jit_debug_descriptor.first_entry = eo->entry.next_entry; |
| if (eo->entry.next_entry) |
| eo->entry.next_entry->prev_entry = eo->entry.prev_entry; |
| __jit_debug_descriptor.relevant_entry = &eo->entry; |
| __jit_debug_descriptor.action_flag = GDBJIT_UNREGISTER; |
| __jit_debug_register_code(); |
| lj_mem_free(J2G(J), eo, eo->sz); |
| } |
| } |
| |
| #endif |
| #endif |