2018-03-25 05:52:04 +02:00
|
|
|
/***************************************************************************
|
|
|
|
* ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk
|
|
|
|
* Updated, adapted and several bug fixes on 2018 by Eduardo José Tagle
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
**************************************************************************/
|
|
|
|
|
2018-03-28 20:13:20 +02:00
|
|
|
#if defined(__arm__) || defined(__thumb__)
|
2018-03-25 05:52:04 +02:00
|
|
|
|
|
|
|
#define MODULE_NAME "UNWARM"
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "unwarm.h"
|
|
|
|
#include "unwarmmem.h"
|
|
|
|
|
|
|
|
#if defined(UNW_DEBUG)
|
|
|
|
|
2018-04-01 01:48:51 +02:00
|
|
|
/**
|
|
|
|
* Printf wrapper.
|
2018-03-25 05:52:04 +02:00
|
|
|
* 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
|
|
|
|
|
2018-04-01 01:48:51 +02:00
|
|
|
/**
|
|
|
|
* Invalidate all general purpose registers.
|
2018-03-25 05:52:04 +02:00
|
|
|
*/
|
|
|
|
void UnwInvalidateRegisterFile(RegData *regFile) {
|
|
|
|
uint8_t t = 0;
|
|
|
|
do {
|
|
|
|
regFile[t].o = REG_VAL_INVALID;
|
|
|
|
t++;
|
2018-04-05 03:23:41 +02:00
|
|
|
} while (t < 13);
|
2018-03-25 05:52:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-01 01:48:51 +02:00
|
|
|
/**
|
|
|
|
* Initialise the data used for unwinding.
|
2018-03-25 05:52:04 +02:00
|
|
|
*/
|
|
|
|
void UnwInitState(UnwState * const state, /**< Pointer to structure to fill. */
|
|
|
|
const UnwindCallbacks *cb, /**< Callbacks. */
|
|
|
|
void *rptData, /**< Data to pass to report function. */
|
|
|
|
uint32_t pcValue, /**< PC at which to start unwinding. */
|
|
|
|
uint32_t 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));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Detect if function names are available
|
|
|
|
static int __attribute__ ((noinline)) has_function_names(void) {
|
2018-03-26 08:42:54 +02:00
|
|
|
uint32_t flag_word = ((uint32_t*)(((uint32_t)(&has_function_names)) & (-4))) [-1];
|
2018-03-25 05:52:04 +02:00
|
|
|
return ((flag_word & 0xff000000) == 0xff000000) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
2018-04-01 01:48:51 +02:00
|
|
|
/**
|
|
|
|
* Call the report function to indicate some return address.
|
2018-03-25 05:52:04 +02:00
|
|
|
* This returns the value of the report function, which if true
|
|
|
|
* indicates that unwinding may continue.
|
|
|
|
*/
|
|
|
|
bool UnwReportRetAddr(UnwState * const state, uint32_t addr) {
|
|
|
|
|
|
|
|
UnwReport entry;
|
|
|
|
|
|
|
|
// We found two acceptable values.
|
|
|
|
entry.name = NULL;
|
2018-03-26 08:42:54 +02:00
|
|
|
entry.address = addr & 0xFFFFFFFE; // Remove Thumb bit
|
2018-03-25 05:52:04 +02:00
|
|
|
entry.function = 0;
|
|
|
|
|
|
|
|
// If there are function names, try to solve name
|
|
|
|
if (has_function_names()) {
|
|
|
|
|
|
|
|
// Lets find the function name, if possible
|
|
|
|
|
|
|
|
// Align address to 4 bytes
|
|
|
|
uint32_t pf = addr & (-4);
|
|
|
|
|
|
|
|
// Scan backwards until we find the function name
|
|
|
|
uint32_t v;
|
2018-04-05 03:23:41 +02:00
|
|
|
while (state->cb->readW(pf-4,&v)) {
|
2018-03-25 05:52:04 +02:00
|
|
|
|
2018-03-26 08:42:54 +02:00
|
|
|
// Check if name descriptor is valid
|
2018-04-01 01:48:51 +02:00
|
|
|
if ((v & 0xFFFFFF00) == 0xFF000000 && (v & 0xFF) > 1) {
|
2018-03-25 05:52:04 +02:00
|
|
|
// Assume the name was found!
|
2018-04-01 01:48:51 +02:00
|
|
|
entry.name = ((const char*)pf) - 4 - (v & 0xFF);
|
2018-03-25 05:52:04 +02:00
|
|
|
entry.function = pf;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Go backwards to the previous word
|
2018-04-05 03:23:41 +02:00
|
|
|
pf -= 4;
|
2018-03-25 05:52:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cast away const from reportData.
|
|
|
|
* The const is only to prevent the unw module modifying the data.
|
|
|
|
*/
|
|
|
|
return state->cb->report((void *)state->reportData, &entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-01 01:48:51 +02:00
|
|
|
/**
|
|
|
|
* Write some register to memory.
|
2018-03-25 05:52:04 +02:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
bool UnwMemWriteRegister(UnwState * const state, const uint32_t addr, const RegData * const reg) {
|
|
|
|
return UnwMemHashWrite(&state->memData, addr, reg->v, M_IsOriginValid(reg->o));
|
|
|
|
}
|
|
|
|
|
2018-04-01 01:48:51 +02:00
|
|
|
/**
|
|
|
|
* Read a register from memory.
|
2018-03-25 05:52:04 +02:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
bool UnwMemReadRegister(UnwState * const state, const uint32_t addr, RegData * const reg) {
|
|
|
|
bool tracked;
|
|
|
|
|
2018-04-01 01:48:51 +02:00
|
|
|
// Check if the value can be found in the hash
|
|
|
|
if (UnwMemHashRead(&state->memData, addr, ®->v, &tracked)) {
|
2018-03-25 05:52:04 +02:00
|
|
|
reg->o = tracked ? REG_VAL_FROM_MEMORY : REG_VAL_INVALID;
|
|
|
|
return true;
|
|
|
|
}
|
2018-04-01 01:48:51 +02:00
|
|
|
else if (state->cb->readW(addr, ®->v)) { // Not in the hash, so read from real memory
|
2018-03-25 05:52:04 +02:00
|
|
|
reg->o = REG_VAL_FROM_MEMORY;
|
|
|
|
return true;
|
|
|
|
}
|
2018-04-01 01:48:51 +02:00
|
|
|
else return false; // Not in the hash, and failed to read from memory
|
2018-03-25 05:52:04 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|