/**************************************************************** * * * Copyright 2001, 2010 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 "i386.h" #include "urx.h" #include "rtnhdr.h" #include "op.h" #include "auto_zlink.h" #define PEA_SZ 5 #define XFER_BYTE_SZ 3 #define XFER_LONG_SZ 6 #define INST_SZ 1 rhdtyp *auto_zlink (unsigned char *pc, int4 **line) { char *adj_pc; /* address of PEA rtnref offset */ mstr rname; mident_fixed rname_local; urx_rtnref *rtnurx; mval rtn; rhdtyp *rhead; error_def (ERR_ROUTINEUNKNOWN); error_def (ERR_LABELUNKNOWN); union { ModR_M modrm; unsigned char byte; } modrm_byte_byte, modrm_byte_long; /* ASSUMPTION -- The instruction previous to the current mpc is a transfer table jump. * This is either a byte or a int4 displacement off of ebx, instruction * size either 3 or 6 (prefix byte, ModR/M byte, 8- or 32-bit offset). */ modrm_byte_byte.modrm.reg_opcode = I386_INS_CALL_Ev; modrm_byte_byte.modrm.mod = I386_MOD32_BASE_DISP_8; modrm_byte_byte.modrm.r_m = I386_REG_EBX; modrm_byte_long.modrm.reg_opcode = I386_INS_CALL_Ev; modrm_byte_long.modrm.mod = I386_MOD32_BASE_DISP_32; modrm_byte_long.modrm.r_m = I386_REG_EBX; if (*(pc - XFER_BYTE_SZ) == I386_INS_Grp5_Prefix && *(pc - XFER_BYTE_SZ + 1) == modrm_byte_byte.byte) { assert (*(pc - XFER_BYTE_SZ - PEA_SZ) == I386_INS_PUSH_Iv); adj_pc = (char *)pc - XFER_BYTE_SZ - PEA_SZ; } else if (*(pc - XFER_LONG_SZ) == I386_INS_Grp5_Prefix && *(pc - XFER_LONG_SZ + 1) == modrm_byte_long.byte) { assert (*(pc - XFER_LONG_SZ - PEA_SZ) == I386_INS_PUSH_Iv); adj_pc = (char *)pc - XFER_LONG_SZ - PEA_SZ; } else GTMASSERT; if (azl_geturxrtn (adj_pc + INST_SZ, &rname, &rtnurx)) { assert (0 <= rname.len && rname.len <= MAX_MIDENT_LEN); assert (rname.addr); /* Copy rname into local storage because azl_geturxrtn sets rname.addr to an address that is freed during op_zlink and before the call to find_rtn_hdr. */ memcpy(rname_local.c, rname.addr, rname.len); rname.addr = rname_local.c; assert (rtnurx); assert (*(adj_pc - PEA_SZ) == I386_INS_PUSH_Iv); assert (azl_geturxlab (adj_pc - PEA_SZ + INST_SZ, rtnurx)); assert (!find_rtn_hdr (&rname)); rtn.mvtype = MV_STR; rtn.str.len = rname.len; rtn.str.addr = rname.addr; op_zlink (&rtn, 0); if (0 != (rhead = find_rtn_hdr (&rname))) { *line = *(int4 **) (adj_pc - PEA_SZ + INST_SZ); if (!(*line)) rts_error(VARLSTCNT(1) ERR_LABELUNKNOWN); return rhead; } } rts_error(VARLSTCNT(1) ERR_ROUTINEUNKNOWN); return NULL; }