RELEASE LuaJIT-2.0.0-beta1
This commit is contained in:
739
src/lj_gdbjit.c
Normal file
739
src/lj_gdbjit.c
Normal file
@@ -0,0 +1,739 @@
|
||||
/*
|
||||
** Client for the GDB JIT API.
|
||||
** Copyright (C) 2005-2009 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_str.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_def_cfa = 0xc,
|
||||
DW_CFA_def_cfa_offset = 0xe,
|
||||
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,
|
||||
#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 defined(__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(__solaris__)
|
||||
.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,
|
||||
#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. */
|
||||
Trace *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. */
|
||||
const char *trname; /* Name of trace. */
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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, ctx->trname);
|
||||
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));
|
||||
DB(DW_CFA_offset|DW_REG_RA); DUV(1);
|
||||
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
|
||||
/* Add saved registers for x64 CFRAME. */
|
||||
#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, Trace *T, TraceNo traceno)
|
||||
{
|
||||
GDBJITctx ctx;
|
||||
lua_State *L = J->L;
|
||||
GCproto *pt = &gcref(T->startpt)->pt;
|
||||
TraceNo parent = T->ir[REF_BASE].op1;
|
||||
uintptr_t pcofs = (uintptr_t)(T->snap[0].mapofs+T->snap[0].nslots);
|
||||
const BCIns *startpc = (const BCIns *)(uintptr_t)T->snapmap[pcofs];
|
||||
ctx.T = T;
|
||||
ctx.mcaddr = (uintptr_t)T->mcode;
|
||||
ctx.szmcode = T->szmcode;
|
||||
ctx.spadjp = CFRAME_SIZE + (MSize)(parent ? J->trace[parent]->spadjust : 0);
|
||||
ctx.spadj = CFRAME_SIZE + T->spadjust;
|
||||
ctx.lineno = pt->lineinfo ? pt->lineinfo[startpc - pt->bc] : 0;
|
||||
ctx.filename = strdata(pt->chunkname);
|
||||
if (*ctx.filename == '@' || *ctx.filename == '=')
|
||||
ctx.filename++;
|
||||
else
|
||||
ctx.filename = "(string)";
|
||||
ctx.trname = lj_str_pushf(L, "TRACE_%d", traceno);
|
||||
L->top--;
|
||||
gdbjit_buildobj(&ctx);
|
||||
gdbjit_newentry(L, &ctx);
|
||||
}
|
||||
|
||||
/* Delete debug info for trace and notify GDB. */
|
||||
void lj_gdbjit_deltrace(jit_State *J, Trace *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
|
||||
Reference in New Issue
Block a user