b4eab59951
Simplified stack unwinder for ARM. This is port of http://www.mcternan.me.uk/ArmStackUnwinding/ backtrace() is called from UIE() on native targets and from panicf() on both native and ARM RaaA. Change-Id: I8e4b3c02490dd60b30aa372fe842d193b8929ce0
183 lines
6.1 KiB
C
183 lines
6.1 KiB
C
/***************************************************************************
|
|
* ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk
|
|
*
|
|
* This program is PUBLIC DOMAIN.
|
|
* This means that there is no copyright and anyone is able to take a copy
|
|
* for free and use it as they wish, with or without modifications, and in
|
|
* any context, commercially or otherwise. The only limitation is that I
|
|
* don't guarantee that the software is fit for any purpose or accept any
|
|
* liability for it's use or misuse - this software is without warranty.
|
|
***************************************************************************
|
|
* File Description: Utility functions and glue for ARM unwinding sub-modules.
|
|
**************************************************************************/
|
|
|
|
#define MODULE_NAME "UNWARM"
|
|
|
|
/***************************************************************************
|
|
* Include Files
|
|
**************************************************************************/
|
|
|
|
#include "types.h"
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include "unwarm.h"
|
|
#include "unwarmmem.h"
|
|
|
|
/***************************************************************************
|
|
* Manifest Constants
|
|
**************************************************************************/
|
|
|
|
|
|
/***************************************************************************
|
|
* Type Definitions
|
|
**************************************************************************/
|
|
|
|
|
|
/***************************************************************************
|
|
* Variables
|
|
**************************************************************************/
|
|
|
|
|
|
/***************************************************************************
|
|
* Macros
|
|
**************************************************************************/
|
|
|
|
|
|
/***************************************************************************
|
|
* Local Functions
|
|
**************************************************************************/
|
|
|
|
|
|
/***************************************************************************
|
|
* Global Functions
|
|
**************************************************************************/
|
|
|
|
#if defined(UNW_DEBUG)
|
|
/** Printf wrapper.
|
|
* This is used such that alternative outputs for any output can be selected
|
|
* by modification of this wrapper function.
|
|
*/
|
|
void UnwPrintf(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start( args, format );
|
|
vprintf(format, args );
|
|
}
|
|
#endif
|
|
|
|
/** Invalidate all general purpose registers.
|
|
*/
|
|
void UnwInvalidateRegisterFile(RegData *regFile)
|
|
{
|
|
Int8 t = 0;
|
|
|
|
do
|
|
{
|
|
regFile[t].o = REG_VAL_INVALID;
|
|
t++;
|
|
}
|
|
while(t < 13);
|
|
|
|
}
|
|
|
|
|
|
/** Initialise the data used for unwinding.
|
|
*/
|
|
void UnwInitState(UnwState * const state, /**< Pointer to structure to fill. */
|
|
const UnwindCallbacks *cb, /**< Callbacks. */
|
|
void *rptData, /**< Data to pass to report function. */
|
|
Int32 pcValue, /**< PC at which to start unwinding. */
|
|
Int32 spValue) /**< SP at which to start unwinding. */
|
|
{
|
|
UnwInvalidateRegisterFile(state->regData);
|
|
|
|
/* Store the pointer to the callbacks */
|
|
state->cb = cb;
|
|
state->reportData = rptData;
|
|
|
|
/* Setup the SP and PC */
|
|
state->regData[13].v = spValue;
|
|
state->regData[13].o = REG_VAL_FROM_CONST;
|
|
state->regData[15].v = pcValue;
|
|
state->regData[15].o = REG_VAL_FROM_CONST;
|
|
|
|
UnwPrintd3("\nInitial: PC=0x%08x SP=0x%08x\n", pcValue, spValue);
|
|
|
|
/* Invalidate all memory addresses */
|
|
memset(state->memData.used, 0, sizeof(state->memData.used));
|
|
}
|
|
|
|
|
|
/** Call the report function to indicate some return address.
|
|
* This returns the value of the report function, which if TRUE
|
|
* indicates that unwinding may continue.
|
|
*/
|
|
Boolean UnwReportRetAddr(UnwState * const state, Int32 addr)
|
|
{
|
|
/* Cast away const from reportData.
|
|
* The const is only to prevent the unw module modifying the data.
|
|
*/
|
|
return state->cb->report((void *)state->reportData, addr);
|
|
}
|
|
|
|
|
|
/** Write some register to memory.
|
|
* This will store some register and meta data onto the virtual stack.
|
|
* The address for the write
|
|
* \param state [in/out] The unwinding state.
|
|
* \param wAddr [in] The address at which to write the data.
|
|
* \param reg [in] The register to store.
|
|
* \return TRUE if the write was successful, FALSE otherwise.
|
|
*/
|
|
Boolean UnwMemWriteRegister(UnwState * const state,
|
|
const Int32 addr,
|
|
const RegData * const reg)
|
|
{
|
|
return UnwMemHashWrite(&state->memData,
|
|
addr,
|
|
reg->v,
|
|
M_IsOriginValid(reg->o));
|
|
}
|
|
|
|
/** Read a register from memory.
|
|
* This will read a register from memory, and setup the meta data.
|
|
* If the register has been previously written to memory using
|
|
* UnwMemWriteRegister, the local hash will be used to return the
|
|
* value while respecting whether the data was valid or not. If the
|
|
* register was previously written and was invalid at that point,
|
|
* REG_VAL_INVALID will be returned in *reg.
|
|
* \param state [in] The unwinding state.
|
|
* \param addr [in] The address to read.
|
|
* \param reg [out] The result, containing the data value and the origin
|
|
* which will be REG_VAL_FROM_MEMORY, or REG_VAL_INVALID.
|
|
* \return TRUE if the address could be read and *reg has been filled in.
|
|
* FALSE is the data could not be read.
|
|
*/
|
|
Boolean UnwMemReadRegister(UnwState * const state,
|
|
const Int32 addr,
|
|
RegData * const reg)
|
|
{
|
|
Boolean tracked;
|
|
|
|
/* Check if the value can be found in the hash */
|
|
if(UnwMemHashRead(&state->memData, addr, ®->v, &tracked))
|
|
{
|
|
reg->o = tracked ? REG_VAL_FROM_MEMORY : REG_VAL_INVALID;
|
|
return TRUE;
|
|
}
|
|
/* Not in the hash, so read from real memory */
|
|
else if(state->cb->readW(addr, ®->v))
|
|
{
|
|
reg->o = REG_VAL_FROM_MEMORY;
|
|
return TRUE;
|
|
}
|
|
/* Not in the hash, and failed to read from memory */
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* END OF FILE */
|