/* +----------------------------------------------------------------------+ | Zend JIT | +----------------------------------------------------------------------+ | Copyright (c) The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | https://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Dmitry Stogov | | Xinchen Hui | +----------------------------------------------------------------------+ | Based on Mike Pall's implementation of GDB interface for LuaJIT. | | LuaJIT -- a Just-In-Time Compiler for Lua. http://luajit.org/ | +----------------------------------------------------------------------+ */ #define HAVE_GDB #include "zend_jit_gdb.h" #include "zend_elf.h" #include "zend_gdb.h" /* DWARF definitions. */ #define DW_CIE_VERSION 1 /* CFA (Canonical frame address) */ 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 = 0x03, 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 defined(__i386__) 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 defined(__x86_64__) /* 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 defined(__aarch64__) DW_REG_SP = 31, DW_REG_RA = 30, DW_REG_X29 = 29, #else #error "Unsupported target architecture" #endif }; 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 }; typedef struct _zend_gdbjit_obj { zend_elf_header hdr; zend_elf_sectheader sect[GDBJIT_SECT__MAX]; zend_elf_symbol sym[GDBJIT_SYM__MAX]; uint8_t space[4096]; } zend_gdbjit_obj; static const zend_elf_header zend_elfhdr_template = { .emagic = { 0x7f, 'E', 'L', 'F' }, #ifdef ELF64 .eclass = 2, #else .eclass = 1, #endif #ifdef WORDS_BIGENDIAN .eendian = 2, #else .eendian = 1, #endif .eversion = 1, #if defined(Linux) .eosabi = 0, /* Nope, it's not 3. ??? */ #elif defined(__FreeBSD__) .eosabi = 9, #elif defined(__OpenBSD__) .eosabi = 12, #elif defined(__NetBSD__) .eosabi = 2, #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 defined(__i386__) .machine = 3, #elif defined(__x86_64__) .machine = 62, #elif defined(__aarch64__) .machine = 183, #else # error "Unsupported target architecture" #endif .version = 1, .entry = 0, .phofs = 0, .shofs = offsetof(zend_gdbjit_obj, sect), .flags = 0, .ehsize = sizeof(zend_elf_header), .phentsize = 0, .phnum = 0, .shentsize = sizeof(zend_elf_sectheader), .shnum = GDBJIT_SECT__MAX, .shstridx = GDBJIT_SECT_shstrtab }; /* Context for generating the ELF object for the GDB JIT API. */ typedef struct _zend_gdbjit_ctx { uint8_t *p; /* Pointer to next address in obj.space. */ uint8_t *startp; /* Pointer to start address in obj.space. */ uintptr_t mcaddr; /* Machine code address. */ uint32_t szmcode; /* Size of machine code. */ int32_t lineno; /* Starting line number. */ const char *name; /* JIT function name */ const char *filename; /* Starting file name. */ size_t objsize; /* Final size of ELF object. */ zend_gdbjit_obj obj; /* In-memory ELF object. */ } zend_gdbjit_ctx; /* Add a zero-terminated string */ static uint32_t zend_gdbjit_strz(zend_gdbjit_ctx *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 zend_gdbjit_uleb128(zend_gdbjit_ctx *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 zend_gdbjit_sleb128(zend_gdbjit_ctx *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; } static void zend_gdbjit_secthdr(zend_gdbjit_ctx *ctx) { zend_elf_sectheader *sect; *ctx->p++ = '\0'; #define SECTDEF(id, tp, al) \ sect = &ctx->obj.sect[GDBJIT_SECT_##id]; \ sect->name = zend_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(zend_gdbjit_obj, sym); sect->size = sizeof(ctx->obj.sym); sect->link = GDBJIT_SECT_strtab; sect->entsize = sizeof(zend_elf_symbol); sect->info = GDBJIT_SYM_FUNC; SECTDEF(debug_info, PROGBITS, 1); SECTDEF(debug_abbrev, PROGBITS, 1); SECTDEF(debug_line, PROGBITS, 1); #undef SECTDEF } static void zend_gdbjit_symtab(zend_gdbjit_ctx *ctx) { zend_elf_symbol *sym; *ctx->p++ = '\0'; sym = &ctx->obj.sym[GDBJIT_SYM_FILE]; sym->name = zend_gdbjit_strz(ctx, "JIT code"); sym->sectidx = ELFSECT_IDX_ABS; sym->info = ELFSYM_INFO(ELFSYM_BIND_LOCAL, ELFSYM_TYPE_FILE); sym = &ctx->obj.sym[GDBJIT_SYM_FUNC]; sym->name = zend_gdbjit_strz(ctx, ctx->name); sym->sectidx = GDBJIT_SECT_text; sym->value = 0; sym->size = ctx->szmcode; sym->info = ELFSYM_INFO(ELFSYM_BIND_GLOBAL, ELFSYM_TYPE_FUNC); } typedef ZEND_SET_ALIGNED(1, uint16_t unaligned_uint16_t); typedef ZEND_SET_ALIGNED(1, uint32_t unaligned_uint32_t); typedef ZEND_SET_ALIGNED(1, uintptr_t unaligned_uintptr_t); #define SECTALIGN(p, a) \ ((p) = (uint8_t *)(((uintptr_t)(p) + ((a)-1)) & ~(uintptr_t)((a)-1))) /* Shortcuts to generate DWARF structures. */ #define DB(x) (*p++ = (x)) #define DI8(x) (*(int8_t *)p = (x), p++) #define DU16(x) (*(unaligned_uint16_t *)p = (x), p += 2) #define DU32(x) (*(unaligned_uint32_t *)p = (x), p += 4) #define DADDR(x) (*(unaligned_uintptr_t *)p = (x), p += sizeof(uintptr_t)) #define DUV(x) (ctx->p = p, zend_gdbjit_uleb128(ctx, (x)), p = ctx->p) #define DSV(x) (ctx->p = p, zend_gdbjit_sleb128(ctx, (x)), p = ctx->p) #define DSTR(str) (ctx->p = p, zend_gdbjit_strz(ctx, (str)), p = ctx->p) #define DALIGNNOP(s) while ((uintptr_t)p & ((s)-1)) *p++ = DW_CFA_nop #define DSECT(name, stmt) \ { unaligned_uint32_t *szp_##name = (uint32_t *)p; p += 4; stmt \ *szp_##name = (uint32_t)((p-(uint8_t *)szp_##name)-4); } static void zend_gdbjit_ehframe(zend_gdbjit_ctx *ctx, uint32_t sp_offset, uint32_t sp_adjustment) { uint8_t *p = ctx->p; uint8_t *framep = p; /* DWARF EH CIE (Common Information Entry) */ DSECT(CIE, DU32(0); /* CIE ID. */ DB(DW_CIE_VERSION); /* Version */ DSTR("zR"); /* Augmentation String. */ 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. */ #if defined(__x86_64__) || defined(i386) DB(DW_CFA_def_cfa); DUV(DW_REG_SP); DUV(sizeof(uintptr_t)); DB(DW_CFA_offset|DW_REG_RA); DUV(1); #elif defined(__aarch64__) DB(DW_CFA_def_cfa); DUV(DW_REG_SP); DUV(0); #endif DALIGNNOP(sizeof(uintptr_t)); ) /* DWARF EH FDE (Frame Description Entry). */ DSECT(FDE, DU32((uint32_t)(p-framep)); /* Offset to CIE Pointer. */ DU32(0); /* Machine code offset relative to .text. */ DU32(ctx->szmcode); /* Machine code length. */ DB(0); /* Augmentation data. */ DB(DW_CFA_def_cfa_offset); DUV(sp_offset); #if defined(__aarch64__) if (sp_offset) { if (sp_adjustment && sp_adjustment < sp_offset) { DB(DW_CFA_offset|DW_REG_X29); DUV(sp_adjustment / sizeof(uintptr_t)); DB(DW_CFA_offset|DW_REG_RA); DUV((sp_adjustment / sizeof(uintptr_t)) - 1); } else { DB(DW_CFA_offset|DW_REG_X29); DUV(sp_offset / sizeof(uintptr_t)); DB(DW_CFA_offset|DW_REG_RA); DUV((sp_offset / sizeof(uintptr_t)) - 1); } } #endif if (sp_adjustment && sp_adjustment > sp_offset) { DB(DW_CFA_advance_loc|1); DB(DW_CFA_def_cfa_offset); DUV(sp_adjustment); #if defined(__aarch64__) if (!sp_offset) { DB(DW_CFA_offset|DW_REG_X29); DUV(sp_adjustment / sizeof(uintptr_t)); DB(DW_CFA_offset|DW_REG_RA); DUV((sp_adjustment / sizeof(uintptr_t)) - 1); } #endif } DALIGNNOP(sizeof(uintptr_t)); ) ctx->p = p; } static void zend_gdbjit_debuginfo(zend_gdbjit_ctx *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; } static void zend_gdbjit_debugabbrev(zend_gdbjit_ctx *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))) static void zend_gdbjit_debugline(zend_gdbjit_ctx *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 typedef void (*zend_gdbjit_initf) (zend_gdbjit_ctx *ctx); static void zend_gdbjit_initsect(zend_gdbjit_ctx *ctx, int sect) { ctx->startp = ctx->p; ctx->obj.sect[sect].ofs = (uintptr_t)((char *)ctx->p - (char *)&ctx->obj); } static void zend_gdbjit_initsect_done(zend_gdbjit_ctx *ctx, int sect) { ctx->obj.sect[sect].size = (uintptr_t)(ctx->p - ctx->startp); } static void zend_gdbjit_buildobj(zend_gdbjit_ctx *ctx, uint32_t sp_offset, uint32_t sp_adjustment) { zend_gdbjit_obj *obj = &ctx->obj; /* Fill in ELF header and clear structures. */ memcpy(&obj->hdr, &zend_elfhdr_template, sizeof(zend_elf_header)); memset(&obj->sect, 0, sizeof(zend_elf_sectheader) * GDBJIT_SECT__MAX); memset(&obj->sym, 0, sizeof(zend_elf_symbol) * GDBJIT_SYM__MAX); /* Initialize sections. */ ctx->p = obj->space; zend_gdbjit_initsect(ctx, GDBJIT_SECT_shstrtab); zend_gdbjit_secthdr(ctx); zend_gdbjit_initsect_done(ctx, GDBJIT_SECT_shstrtab); zend_gdbjit_initsect(ctx, GDBJIT_SECT_strtab); zend_gdbjit_symtab(ctx); zend_gdbjit_initsect_done(ctx, GDBJIT_SECT_strtab); zend_gdbjit_initsect(ctx, GDBJIT_SECT_debug_info); zend_gdbjit_debuginfo(ctx); zend_gdbjit_initsect_done(ctx, GDBJIT_SECT_debug_info); zend_gdbjit_initsect(ctx, GDBJIT_SECT_debug_abbrev); zend_gdbjit_debugabbrev(ctx); zend_gdbjit_initsect_done(ctx, GDBJIT_SECT_debug_abbrev); zend_gdbjit_initsect(ctx, GDBJIT_SECT_debug_line); zend_gdbjit_debugline(ctx); zend_gdbjit_initsect_done(ctx, GDBJIT_SECT_debug_line); SECTALIGN(ctx->p, sizeof(uintptr_t)); zend_gdbjit_initsect(ctx, GDBJIT_SECT_eh_frame); zend_gdbjit_ehframe(ctx, sp_offset, sp_adjustment); zend_gdbjit_initsect_done(ctx, GDBJIT_SECT_eh_frame); ctx->objsize = (size_t)((char *)ctx->p - (char *)obj); ZEND_ASSERT(ctx->objsize < sizeof(zend_gdbjit_obj)); } int zend_jit_gdb_register(const char *name, const zend_op_array *op_array, const void *start, size_t size, uint32_t sp_offset, uint32_t sp_adjustment) { zend_gdbjit_ctx ctx; ctx.mcaddr = (uintptr_t)start; ctx.szmcode = (uint32_t)size; ctx.name = name; ctx.filename = op_array ? ZSTR_VAL(op_array->filename) : "unknown"; ctx.lineno = op_array ? op_array->line_start : 0; zend_gdbjit_buildobj(&ctx, sp_offset, sp_adjustment); return zend_gdb_register_code(&ctx.obj, ctx.objsize); } int zend_jit_gdb_unregister(void) { zend_gdb_unregister_all(); return 1; } void zend_jit_gdb_init(void) { #if 0 /* This might enable registration of all JIT-ed code, but unfortunately, * in case of many functions, this takes enormous time. */ if (zend_gdb_present()) { JIT_G(debug) |= ZEND_JIT_DEBUG_GDB; } #endif }