Tons of fixes to the backtracker code, and also added an extra backtracker that does not require unwind tables to work and it is used if unwind tables are absent
This commit is contained in:
179
Marlin/src/HAL/HAL_DUE/backtrace/unwarm.c
Normal file
179
Marlin/src/HAL/HAL_DUE/backtrace/unwarm.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/***************************************************************************
|
||||
* 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.
|
||||
**************************************************************************/
|
||||
|
||||
#ifdef ARDUINO_ARCH_SAM
|
||||
|
||||
#define MODULE_NAME "UNWARM"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include "unwarm.h"
|
||||
#include "unwarmmem.h"
|
||||
|
||||
#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) {
|
||||
|
||||
uint8_t 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. */
|
||||
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) {
|
||||
|
||||
uint32_t flag_word = ((uint32_t*)&has_function_names)[-1];
|
||||
return ((flag_word & 0xff000000) == 0xff000000) ? 1 : 0;
|
||||
}
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
bool UnwReportRetAddr(UnwState * const state, uint32_t addr) {
|
||||
|
||||
UnwReport entry;
|
||||
|
||||
// We found two acceptable values.
|
||||
entry.name = NULL;
|
||||
entry.address = addr;
|
||||
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;
|
||||
while(state->cb->readW(pf-4,&v)) {
|
||||
|
||||
// Check if name descriptor is valid and name is terminated in 0.
|
||||
if ((v & 0xffffff00) == 0xff000000 &&
|
||||
(v & 0xff) > 1) {
|
||||
|
||||
// Assume the name was found!
|
||||
entry.name = ((const char*)pf) - 4 - (v & 0xff);
|
||||
entry.function = pf;
|
||||
break;
|
||||
}
|
||||
|
||||
// Go backwards to the previous word
|
||||
pf -= 4;;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
bool UnwMemWriteRegister(UnwState * const state, const uint32_t 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.
|
||||
*/
|
||||
bool UnwMemReadRegister(UnwState * const state, const uint32_t addr, RegData * const reg) {
|
||||
|
||||
bool 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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user