/**************************************************************** * * * Copyright 2010, 2011 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "gtm_string.h" #include "rtnhdr.h" #include "stack_frame.h" #include "op.h" #include "get_ret_targ.h" #include "xfer_enum.h" #include "dollar_quit.h" #if defined(__sparc) # include "sparc.h" #elif defined(__s390__) || defined(__MVS__) # include "s390.h" #elif defined(__hppa) # include "hppa.h" #elif defined(__ia64) # include "ia64.h" #endif GBLREF int process_exiting; /* Determine value to return for $QUIT: * * 0 - no return value requested * 1 - non-alias return value requested * 3 - alias return value requrested * * Determination of parm/no-parm is made by calling get_ret_targ() which searches the mv_stents * on the stack back to a counted frame to see of an MVST_PARM block containg a return mval was pushed * onto the stack signifying a return value is required. If a return value is required, determination of * the type of return value is made by examining the generated instruction stream at the return point * and checking for an OC_EXFUNRET or OC_EXFUNRETALS (non-alias and alias type return var processor * respectively) opcode following the return point. This is done by isolating the instruction that * indexes into the transfer table, extracting the xfer-table index and checking against known values * for op_exfunret and op_exfunretals to determine type of return. No match means no return value. * * Because this routine looks at the generated code stream at the return point, it is highly platform * dependent. * * Note: If generated code changes for a platform, this module needs to be revisited. */ int dollar_quit(void) { stack_frame *sf; mval *parm_blk; int retval; int xfer_index; union { unsigned char *instr; unsigned short *instr_type; unsigned char *instr_type_8; unsigned char *xfer_offset_8; short *xfer_offset_16; int *xfer_offset_32; } ptrs; parm_blk = get_ret_targ(&sf); if (NULL == parm_blk) /* There was no parm block - return 0 */ retval = 0; else { /* There is a parm block - see if they want a "regular" or alias type return argument */ sf = sf->old_frame_pointer; /* Caller's frame */ # ifdef __i386 { ptrs.instr = sf->mpc; /* First figure out the potential length of the lea* instruction loading compiler temp offset */ if (0x078d == *ptrs.instr_type) ptrs.instr += 3; /* Past the 2 byte lea plus 1 byte push */ else if (0x478d == *ptrs.instr_type) ptrs.instr += 4; /* Past the 3 byte lea plus 1 byte push */ else if (0x878d == *ptrs.instr_type) ptrs.instr += 7; /* Past the 6 byte lea plus 1 byte push */ else ptrs.instr = NULL; /* Note the "long format call opcode" check below assumes that both of the EXFUNRET[ALS] calls remain at a * greater-than-128 byte offset in the transfer table (which they currently are). */ if ((NULL != ptrs.instr) && (0x93FF == *ptrs.instr_type)) { ptrs.instr += SIZEOF(*ptrs.instr_type); xfer_index = *ptrs.xfer_offset_32 / SIZEOF(void *); } else xfer_index = -1; } # elif defined(__x86_64__) { ptrs.instr = sf->mpc; if (0x8d49 == *ptrs.instr_type) { ptrs.instr += 2; /* Past first part of instruction type */ if (0x7e == *ptrs.instr_type_8) ptrs.instr += 2; /* past last byte of instruction type plus 1 byte offset */ else if (0xbe == *ptrs.instr_type_8) ptrs.instr += 5; /* past last byte of instruction type plus 4 byte offset */ else ptrs.instr = NULL; } else ptrs.instr_type = NULL; if ((NULL != ptrs.instr) && (0x93FF == *ptrs.instr_type)) { /* Long format CALL */ ptrs.instr += SIZEOF(*ptrs.instr_type); xfer_index = *ptrs.xfer_offset_32 / SIZEOF(void *); } else xfer_index = -1; /* Not an xfer index */ } # elif defined(_AIX) { ptrs.instr = sf->mpc + 4; /* Past address load of compiler temp arg */ if (0xE97C == *ptrs.instr_type) { /* ld of descriptor address from xfer table */ ptrs.instr += SIZEOF(*ptrs.instr_type); xfer_index = *ptrs.xfer_offset_16 / SIZEOF(void *); } else xfer_index = -1; } # elif defined(__alpha) /* Applies to both VMS and Tru64 as have same codegen */ { ptrs.instr = sf->mpc + 4; /* Past address load of compiler temp arg */ if (UNIX_ONLY(0xA36C) VMS_ONLY(0xA36B) == *(ptrs.instr_type + 1)) /* Different code for reg diff */ /* ldl of descriptor address from xfer table - little endian - offset prior to opcode */ xfer_index = *ptrs.xfer_offset_16 / SIZEOF(void *); else xfer_index = -1; } # elif defined(__sparc) { ptrs.instr = sf->mpc + 4; /* Past address load of compiler temp arg */ if (0xC85C == *ptrs.instr_type) { /* ldx of rtn address from xfer table */ ptrs.instr += SIZEOF(*ptrs.instr_type); xfer_index = (*ptrs.xfer_offset_16 & SPARC_MASK_OFFSET) / SIZEOF(void *); } else xfer_index = -1; } # elif defined(__s390__) || defined(__MVS__) { format_RXY instr_LG; ZOS_ONLY(format_RR instr_RR;) union { int offset; struct { /* Used to reassemble the offset in the LG instruction */ int offset_unused:12; int offset_hi:8; int offset_low:12; } instr_LG_bits; } RXY; /* Need to forward space past address load of compiler temp arg. On zOS, the position of the mpc can * differ. If the origin point is an external call, we have to forward space past the BCR following * the call point. If the origin point is an internal call, the call point is a branch with no * following BCR. So zOS needs to determine if it has to jump over a BCR call first. */ ZOS_ONLY(memcpy(&instr_RR, sf->mpc, SIZEOF(instr_RR))); ptrs.instr = sf->mpc; ZOS_ONLY(if ((S390_OPCODE_RR_BCR == instr_RR.opcode) && (0 == instr_RR.r1) && (0 == instr_RR.r2)) ptrs.instr += 2); /* Past BCR 0,0 from external call */ ptrs.instr += 6; /* Past address load of compiler temp arg */ memcpy(&instr_LG, ptrs.instr, SIZEOF(instr_LG)); if ((S390_OPCODE_RXY_LG == instr_LG.opcode) && (S390_SUBCOD_RXY_LG == instr_LG.opcode2) && (GTM_REG_SAVE_RTN_ADDR == instr_LG.r1) && (GTM_REG_XFER_TABLE == instr_LG.b2)) { /* LG of rtn address from xfer table */ RXY.offset = 0; RXY.instr_LG_bits.offset_hi = instr_LG.dh2; RXY.instr_LG_bits.offset_low = instr_LG.dl2; xfer_index = RXY.offset / SIZEOF(void *); } else xfer_index = -1; } # elif defined(__hppa) { hppa_fmt_1 instr_LDX; union { int offset; struct { signed int high:19; unsigned int low:13; } instr_offset; } fmt_1; ptrs.instr = sf->mpc + 8; /* Past address load of compiler temp arg plus rtn call to load of xfer * table call with offset in delay slot */ memcpy(&instr_LDX, ptrs.instr, SIZEOF(instr_LDX)); if (((HPPA_INS_LDW >> HPPA_SHIFT_OP) == instr_LDX.pop) && (GTM_REG_XFER_TABLE == instr_LDX.b) && (R22 == instr_LDX.t)) { /* ldx of rtn address from xfer table */ fmt_1.instr_offset.low = instr_LDX.im14a; fmt_1.instr_offset.high = instr_LDX.im14b; xfer_index = fmt_1.offset / SIZEOF(void *); } else xfer_index = -1; } # elif defined(__ia64) { ia64_bundle xfer_ref_inst; /* Buffer to put built instruction into */ ia64_fmt_A4 adds_inst; /* The actual adds instruction computing xfer reference */ union { int offset; struct { # ifdef BIGENDIAN signed int sign:19; unsigned int imm6d:6; unsigned int imm7b:7; # else unsigned int imm7b:7; unsigned int imm6d:6; signed int sign:19; # endif } instr_offset; } imm14; ptrs.instr = sf->mpc + 16; /* Past address load of compiler temp arg */ # ifdef BIGENDIAN xfer_ref_inst.hexValue.aValue = GTM_BYTESWAP_64(((ia64_bundle *)ptrs.instr)->hexValue.aValue); xfer_ref_inst.hexValue.bValue = GTM_BYTESWAP_64(((ia64_bundle *)ptrs.instr)->hexValue.bValue); # else xfer_ref_inst.hexValue.aValue = ((ia64_bundle *)ptrs.instr)->hexValue.aValue; xfer_ref_inst.hexValue.bValue = ((ia64_bundle *)ptrs.instr)->hexValue.bValue; # endif adds_inst.hexValue = xfer_ref_inst.format.inst3; /* Extract instruction from bundle */ if ((8 == adds_inst.format.pop) && (2 == adds_inst.format.x2a) && (GTM_REG_XFER_TABLE == adds_inst.format.r3) && (IA64_REG_SCRATCH1 == adds_inst.format.r1)) { /* We have an xfer computation instruction. Find the offset to find which opcode */ imm14.instr_offset.imm7b = adds_inst.format.imm7b; /* Low order bits */ imm14.instr_offset.imm6d = adds_inst.format.imm6d; /* upper bits minus sign */ imm14.instr_offset.sign = adds_inst.format.sb; /* Sign bit propagated */ xfer_index = imm14.offset / SIZEOF(void *); } else xfer_index = -1; } # else # error Unsupported Platform # endif if (xf_exfunret == xfer_index) /* Need a QUIT with a non-alias return value */ retval = 1; else if (xf_exfunretals == xfer_index) /* Need a QUIT with an alias return value */ retval = 11; else { /* Something weird afoot - had parm block can can't locate EXFUNRET[ALS] opcode. This can happen if * a fatal error occurs during a call before the callee stack frame is actually pushed and we are * called during GTM_FATAL_ERROR.* file creation. Assert that this is the case, else, we just pretend * we didn't find a parm block.. */ assert(process_exiting); retval = 0; } } return retval; }