diff options
Diffstat (limited to 'sys/contrib/ia64/libuwx/src/uwx_step.c')
-rw-r--r-- | sys/contrib/ia64/libuwx/src/uwx_step.c | 525 |
1 files changed, 525 insertions, 0 deletions
diff --git a/sys/contrib/ia64/libuwx/src/uwx_step.c b/sys/contrib/ia64/libuwx/src/uwx_step.c new file mode 100644 index 0000000..fcbbb5d --- /dev/null +++ b/sys/contrib/ia64/libuwx/src/uwx_step.c @@ -0,0 +1,525 @@ +/* + * Copyright (c) 2002,2003 Hewlett-Packard Company + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "uwx_env.h" +#include "uwx_context.h" +#include "uwx_utable.h" +#include "uwx_uinfo.h" +#include "uwx_scoreboard.h" +#include "uwx_str.h" +#include "uwx_trace.h" + +/* + * uwx_step.c + * + * This file contains the routines for stepping from one frame + * into its callers frame. The context for the current frame + * is maintained inside the current unwind environment + * (struct uwx_env), and is updated with each call to + * uwx_step() to refer to the previous frame. + */ + + +/* Forward Declarations */ + +int uwx_decode_uvec(struct uwx_env *env, uint64_t *uvec, uint64_t **rstate); +int uwx_restore_reg(struct uwx_env *env, uint64_t rstate, + uint64_t *valp, uint64_t *histp); +int uwx_restore_nat(struct uwx_env *env, uint64_t rstate, int unat); + + +/* uwx_get_frame_info: Gets unwind info for current frame */ +static +int uwx_get_frame_info(struct uwx_env *env) +{ + int i; + int status; + int cbstatus; + uint64_t *uvec; + uint64_t *rstate; + struct uwx_utable_entry uentry; + uint64_t uvecout[4]; + + if (env == 0) + return UWX_ERR_NOENV; + if (env->copyin == 0 || env->lookupip == 0) + return UWX_ERR_NOCALLBACKS; + if ((env->context.valid_regs & VALID_BASIC4) != VALID_BASIC4) + return UWX_ERR_NOCONTEXT; + + env->function_offset = -1LL; + env->function_name = 0; + env->module_name = 0; + uwx_reset_str_pool(env); + + /* Use the lookup IP callback routine to find out about the */ + /* current IP. If the predicate registers are valid, pass them */ + /* in the uvec. */ + + i = 0; + if (env->context.valid_regs & (1 << UWX_REG_PREDS)) { + uvecout[i++] = UWX_KEY_PREDS; + uvecout[i++] = env->context.special[UWX_REG_PREDS]; + } + uvecout[i++] = UWX_KEY_END; + uvecout[i++] = 0; + uvec = uvecout; + cbstatus = (*env->lookupip)(UWX_LKUP_LOOKUP, + env->context.special[UWX_REG_IP], env->cb_token, &uvec); + + /* If NOTFOUND, there's nothing we can do but return an error. */ + + if (cbstatus == UWX_LKUP_NOTFOUND) { + status = UWX_ERR_IPNOTFOUND; + } + + /* If the callback returns an unwind table, we need to */ + /* search the table for an unwind entry that describes the */ + /* code region of interest, then decode the unwind information */ + /* associated with that unwind table entry, and store the */ + /* resulting register state array in the unwind environment */ + /* block. */ + + else if (cbstatus == UWX_LKUP_UTABLE) { + status = uwx_search_utable(env, uvec, &uentry); + (void) (*env->lookupip)(UWX_LKUP_FREE, 0, env->cb_token, &uvec); + if (status == UWX_OK) + status = uwx_decode_uinfo(env, &uentry, &rstate); + else if (status == UWX_ERR_NOUENTRY) + status = uwx_default_rstate(env, &rstate); + if (status == UWX_OK) + env->rstate = rstate; + } + + /* If the callback returns a frame description (in the form */ + /* of an update vector), convert the update vector into a */ + /* register state array, then invoke the callback again to */ + /* let it free any memory it allocated. */ + + else if (cbstatus == UWX_LKUP_FDESC) { + status = uwx_decode_uvec(env, uvec, &rstate); + (void) (*env->lookupip)(UWX_LKUP_FREE, 0, env->cb_token, &uvec); + if (status == UWX_OK) + env->rstate = rstate; + } + + /* Any other return from the callback is an error. */ + + else { + status = UWX_ERR_LOOKUPERR; + } + return status; +} + + +/* uwx_get_sym_info: Gets symbolic info from current frame */ +int uwx_get_sym_info( + struct uwx_env *env, + char **modp, + char **symp, + uint64_t *offsetp) +{ + int status; + int cbstatus; + uint64_t ip; + uint64_t *uvec; + uint64_t uvecout[2]; + int i; + + if (env == 0) + return UWX_ERR_NOENV; + if (env->copyin == 0 || env->lookupip == 0) + return UWX_ERR_NOCALLBACKS; + if ((env->context.valid_regs & VALID_BASIC4) != VALID_BASIC4) + return UWX_ERR_NOCONTEXT; + + /* If we haven't already obtained the frame info for the */ + /* current frame, get it now. */ + + if (env->rstate == 0) { + status = uwx_get_frame_info(env); + if (status != UWX_OK) + return status; + } + + /* Get the symbolic information from the lookup IP callback. */ + if (env->function_name == 0) { + ip = env->context.special[UWX_REG_IP]; + i = 0; + if (env->function_offset >= 0) { + uvecout[i++] = UWX_KEY_FUNCSTART; + uvecout[i++] = ip - env->function_offset; + } + uvecout[i++] = UWX_KEY_END; + uvecout[i++] = 0; + uvec = uvecout; + cbstatus = (*env->lookupip)(UWX_LKUP_SYMBOLS, + env->context.special[UWX_REG_IP], env->cb_token, &uvec); + + if (cbstatus == UWX_LKUP_SYMINFO) { + for (i = 0; uvec[i] != UWX_KEY_END; i += 2) { + switch ((int)uvec[i]) { + case UWX_KEY_MODULE: + env->module_name = + uwx_alloc_str(env, (char *)(uvec[i+1])); + break; + case UWX_KEY_FUNC: + env->function_name = + uwx_alloc_str(env, (char *)(uvec[i+1])); + break; + case UWX_KEY_FUNCSTART: + env->function_offset = ip - uvec[i+1]; + break; + } + } + (void) (*env->lookupip)(UWX_LKUP_FREE, 0, env->cb_token, &uvec); + } + } + + *modp = env->module_name; + *symp = env->function_name; + *offsetp = env->function_offset; + + return UWX_OK; +} + + +/* uwx_step: Steps from the current frame to the previous frame */ + +int uwx_step(struct uwx_env *env) +{ + int i; + int status; + int pfs_sol; + int dispcode; + uint64_t val; + uint64_t fval[2]; + uint64_t hist; + uint64_t tempgr[NPRESERVEDGR]; + int needpriunat; + int unat; + int tempnat; + + if (env == 0) + return UWX_ERR_NOENV; + if (env->copyin == 0 || env->lookupip == 0) + return UWX_ERR_NOCALLBACKS; + if ((env->context.valid_regs & VALID_BASIC4) != VALID_BASIC4) + return UWX_ERR_NOCONTEXT; + + /* If we haven't already obtained the frame info for the */ + /* current frame, get it now. */ + + if (env->rstate == 0) { + status = uwx_get_frame_info(env); + if (status != UWX_OK) + return status; + } + + TRACE_S_STEP(env->rstate) + + /* Complete the current context by restoring the current values */ + /* of psp, rp, and pfs. */ + + if (env->rstate[SBREG_PSP] != UWX_DISP_NONE) { + status = uwx_restore_reg(env, env->rstate[SBREG_PSP], &val, &hist); + if (status != UWX_OK) + return status; + env->context.special[UWX_REG_PSP] = val; + env->history.special[UWX_REG_PSP] = hist; + TRACE_S_RESTORE_REG("PSP", env->rstate[SBREG_PSP], val) + } + if (env->rstate[SBREG_RP] != UWX_DISP_NONE) { + status = uwx_restore_reg(env, env->rstate[SBREG_RP], &val, &hist); + if (status != UWX_OK) + return status; + env->context.special[UWX_REG_RP] = val; + env->history.special[UWX_REG_RP] = hist; + TRACE_S_RESTORE_REG("RP", env->rstate[SBREG_RP], val) + } + if (env->rstate[SBREG_PFS] != UWX_DISP_NONE) { + status = uwx_restore_reg(env, env->rstate[SBREG_PFS], &val, &hist); + if (status != UWX_OK) + return status; + env->context.special[UWX_REG_PFS] = val; + env->history.special[UWX_REG_PFS] = hist; + TRACE_S_RESTORE_REG("PFS", env->rstate[SBREG_PFS], val) + } + + /* Check for bottom of stack (rp == 0). */ + + if (env->context.special[UWX_REG_RP] == 0) + return UWX_BOTTOM; + + /* Find where the primary unat is saved, get a copy. */ + /* Then, as we restore the GRs, we'll merge the NaT bits into the */ + /* priunat register in the context. */ + /* (Make sure we need it, though, before we try to get it, */ + /* because the attempt to get it might invoke the copy-in callback. */ + /* We don't need the priunat unless one of GR 4-7 was */ + /* saved to the memory stack.) */ + + needpriunat = 0; + for (i = 0; i < NSB_GR; i++) { + dispcode = UWX_GET_DISP_CODE(env->rstate[SBREG_GR + i]); + if (dispcode == UWX_DISP_SPREL(0) || dispcode == UWX_DISP_PSPREL(0)) + needpriunat = 1; + } + unat = 0; + if (needpriunat && env->rstate[SBREG_PRIUNAT] != UWX_DISP_NONE) { + status = uwx_restore_reg(env, env->rstate[SBREG_PRIUNAT], &val, &hist); + if (status != UWX_OK) + return status; + unat = (int) val; + env->history.special[UWX_REG_PRIUNAT] = hist; + TRACE_S_RESTORE_REG("PRIUNAT", env->rstate[SBREG_PRIUNAT], val) + } + + /* Retrieve saved values of the preserved GRs into temporaries. */ + + tempnat = (int) env->context.special[UWX_REG_PRIUNAT]; + for (i = 0; i < NSB_GR; i++) { + if (env->rstate[SBREG_GR + i] != UWX_DISP_NONE) { + status = uwx_restore_reg(env, + env->rstate[SBREG_GR + i], &val, &hist); + if (status != UWX_OK) + return status; + tempgr[i] = val; + if (uwx_restore_nat(env, env->rstate[SBREG_GR + i], unat)) + tempnat |= 1 << i; + else + tempnat &= ~(1 << i); + env->history.gr[i] = hist; + env->context.valid_regs |= 1 << (i + VALID_GR_SHIFT); + TRACE_S_RESTORE_GR(i, env->rstate[SBREG_GR + i], val) + } + } + + /* Now we have everything we need to step back to the previous frame. */ + + /* Restore preserved BRs. */ + + for (i = 0; i < NSB_BR; i++) { + if (env->rstate[SBREG_BR + i] != UWX_DISP_NONE) { + status = uwx_restore_reg(env, + env->rstate[SBREG_BR + i], &val, &hist); + if (status != UWX_OK) + return status; + env->context.br[i] = val; + env->history.br[i] = hist; + env->context.valid_regs |= 1 << (i + VALID_BR_SHIFT); + TRACE_S_RESTORE_BR(i, env->rstate[SBREG_BR + i], val) + } + } + + /* Restore preserved FRs. */ + + if (env->nsbreg == NSBREG) { + for (i = 0; i < NSB_FR; i++) { + if (env->rstate[SBREG_FR + i] != UWX_DISP_NONE) { + status = uwx_restore_reg(env, + env->rstate[SBREG_FR + i], fval, &hist); + if (status != UWX_OK) + return status; + env->context.fr[i].part0 = fval[0]; + env->context.fr[i].part1 = fval[1]; + env->history.fr[i] = hist; + env->context.valid_frs |= 1 << i; + TRACE_S_RESTORE_FR(i, env->rstate[SBREG_FR + i], fval) + } + } + } + + /* Restore other preserved regs. */ + + if (env->rstate[SBREG_PREDS] != UWX_DISP_NONE) { + status = uwx_restore_reg(env, env->rstate[SBREG_PREDS], &val, &hist); + if (status != UWX_OK) + return status; + env->context.special[UWX_REG_PREDS] = val; + env->history.special[UWX_REG_PREDS] = hist; + env->context.valid_regs |= 1 << UWX_REG_PREDS; + TRACE_S_RESTORE_REG("PREDS", env->rstate[SBREG_PREDS], val) + } + if (env->rstate[SBREG_RNAT] != UWX_DISP_NONE) { + status = uwx_restore_reg(env, env->rstate[SBREG_RNAT], &val, &hist); + if (status != UWX_OK) + return status; + env->context.special[UWX_REG_RNAT] = val; + env->history.special[UWX_REG_RNAT] = hist; + env->context.valid_regs |= 1 << UWX_REG_RNAT; + TRACE_S_RESTORE_REG("RNAT", env->rstate[SBREG_RNAT], val) + } + if (env->rstate[SBREG_UNAT] != UWX_DISP_NONE) { + status = uwx_restore_reg(env, env->rstate[SBREG_UNAT], &val, &hist); + if (status != UWX_OK) + return status; + env->context.special[UWX_REG_UNAT] = val; + env->history.special[UWX_REG_UNAT] = hist; + env->context.valid_regs |= 1 << UWX_REG_UNAT; + TRACE_S_RESTORE_REG("UNAT", env->rstate[SBREG_UNAT], val) + } + if (env->rstate[SBREG_FPSR] != UWX_DISP_NONE) { + status = uwx_restore_reg(env, env->rstate[SBREG_FPSR], &val, &hist); + if (status != UWX_OK) + return status; + env->context.special[UWX_REG_FPSR] = val; + env->history.special[UWX_REG_FPSR] = hist; + env->context.valid_regs |= 1 << UWX_REG_FPSR; + TRACE_S_RESTORE_REG("FPSR", env->rstate[SBREG_FPSR], val) + } + if (env->rstate[SBREG_LC] != UWX_DISP_NONE) { + status = uwx_restore_reg(env, env->rstate[SBREG_LC], &val, &hist); + if (status != UWX_OK) + return status; + env->context.special[UWX_REG_LC] = val; + env->history.special[UWX_REG_LC] = hist; + env->context.valid_regs |= 1 << UWX_REG_LC; + TRACE_S_RESTORE_REG("LC", env->rstate[SBREG_LC], val) + } + + /* Restore preserved GRs from temporaries. */ + + for (i = 0; i < NSB_GR; i++) { + if (env->rstate[SBREG_GR + i] != UWX_DISP_NONE) + env->context.gr[i] = tempgr[i]; + } + env->context.special[UWX_REG_PRIUNAT] = tempnat; + + /* Restore the frame markers. */ + + env->context.special[UWX_REG_IP] = env->context.special[UWX_REG_RP]; + env->history.special[UWX_REG_IP] = env->history.special[UWX_REG_RP]; + + env->context.special[UWX_REG_SP] = env->context.special[UWX_REG_PSP]; + env->history.special[UWX_REG_SP] = env->history.special[UWX_REG_PSP]; + + pfs_sol = ((unsigned int)env->context.special[UWX_REG_PFS] >> 7) & 0x7f; + env->context.special[UWX_REG_BSP] = uwx_add_to_bsp( + env->context.special[UWX_REG_BSP], + -pfs_sol); + + env->context.special[UWX_REG_CFM] = env->context.special[UWX_REG_PFS]; + env->history.special[UWX_REG_CFM] = env->history.special[UWX_REG_PFS]; + + env->context.special[UWX_REG_RP] = 0; + + /* The frame info for the new frame isn't yet available. */ + + env->rstate = 0; + + return UWX_OK; +} + + +/* uwx_decode_uvec: Converts the update vector into a register state array */ + +int uwx_decode_uvec(struct uwx_env *env, uint64_t *uvec, uint64_t **rstate) +{ + while (*uvec != 0) { + switch ((int)*uvec++) { + case UWX_KEY_CONTEXT: + env->abi_context = (int)(*uvec++); + return UWX_ABI_FRAME; + default: + return UWX_ERR_CANTUNWIND; + } + } + return UWX_OK; +} + + +/* uwx_restore_reg: Restores a register according to the scoreboard */ + +#define COPYIN_MSTACK_8(dest, src) \ + (env->remote? \ + (*env->copyin)(UWX_COPYIN_MSTACK, (dest), (src), \ + DWORDSZ, env->cb_token) : \ + (*(uint64_t *)(dest) = *(uint64_t *)(src), DWORDSZ) ) + +int uwx_restore_reg(struct uwx_env *env, uint64_t rstate, + uint64_t *valp, uint64_t *histp) +{ + int status; + uint64_t p; + int n; + int regid; + + status = UWX_OK; + + switch (UWX_GET_DISP_CODE(rstate)) { + case UWX_DISP_SPPLUS(0): + *valp = env->context.special[UWX_REG_SP] + + UWX_GET_DISP_OFFSET(rstate); + *histp = UWX_DISP_NONE; + break; + case UWX_DISP_SPREL(0): + p = env->context.special[UWX_REG_SP] + + UWX_GET_DISP_OFFSET(rstate); + n = COPYIN_MSTACK_8((char *)valp, p); + if (n != DWORDSZ) + status = UWX_ERR_COPYIN_MSTK; + *histp = UWX_DISP_MSTK(p); + break; + case UWX_DISP_PSPREL(0): + p = env->context.special[UWX_REG_PSP] + 16 - + UWX_GET_DISP_OFFSET(rstate); + n = COPYIN_MSTACK_8((char *)valp, p); + if (n != DWORDSZ) + status = UWX_ERR_COPYIN_MSTK; + *histp = UWX_DISP_MSTK(p); + break; + case UWX_DISP_REG(0): + regid = UWX_GET_DISP_REGID(rstate); + status = uwx_get_reg(env, regid, valp); + (void) uwx_get_spill_loc(env, regid, histp); + break; + } + return status; +} + +/* uwx_restore_nat: Returns the saved NaT bit for a preserved GR */ + +int uwx_restore_nat(struct uwx_env *env, uint64_t rstate, int unat) +{ + int nat; + uint64_t p; + + nat = 0; + switch (UWX_GET_DISP_CODE(rstate)) { + case UWX_DISP_SPREL(0): + p = env->context.special[UWX_REG_SP] + + UWX_GET_DISP_OFFSET(rstate); + nat = (unat >> (((int)p >> 3) & 0x3f)) & 0x01; + break; + case UWX_DISP_PSPREL(0): + p = env->context.special[UWX_REG_PSP] + 16 - + UWX_GET_DISP_OFFSET(rstate); + nat = (unat >> (((int)p >> 3) & 0x3f)) & 0x01; + break; + case UWX_DISP_REG(0): + (void) uwx_get_nat(env, UWX_GET_DISP_REGID(rstate), &nat); + break; + } + return nat; +} + |