mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
System: implement libunwind library for RISC-V backtracing
Closes https://github.com/espressif/esp-idf/issues/7866 A minimal x86 implementation has also been added, it is used to perform a host test.
This commit is contained in:
parent
1438d9a02c
commit
eeaa40f71d
@ -15,14 +15,18 @@
|
||||
* found in the official documentation:
|
||||
* http://dwarfstd.org/Download.php
|
||||
*/
|
||||
|
||||
#include "esp_private/eh_frame_parser.h"
|
||||
#include "esp_private/panic_internal.h"
|
||||
#include "sdkconfig.h"
|
||||
#include <string.h>
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
|
||||
|
||||
#include "eh_frame_parser_impl.h"
|
||||
#include "libunwind.h"
|
||||
#include "esp_private/panic_internal.h"
|
||||
#include "esp_private/eh_frame_parser.h"
|
||||
|
||||
#if UNW_UNKNOWN_TARGET
|
||||
#error "Unsupported architecture for unwinding"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Dimension of an array (number of elements)
|
||||
@ -928,4 +932,124 @@ void esp_eh_frame_print_backtrace(const void *frame_or)
|
||||
|
||||
panic_print_str("\r\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* The following functions are the implementation of libunwind API
|
||||
* Check the header libunwind.h for more information
|
||||
*/
|
||||
|
||||
int unw_init_local(unw_cursor_t* c, unw_context_t* ctxt) {
|
||||
/* In our implementation, a context and a cursor is the same, so we simply need
|
||||
* to copy a structure inside another one */
|
||||
_Static_assert(sizeof(unw_cursor_t) >= sizeof(unw_context_t), "unw_cursor_t size must be greater or equal to unw_context_t's");
|
||||
int ret = -UNW_EUNSPEC;
|
||||
if (c != NULL && ctxt != NULL) {
|
||||
memcpy(c, ctxt, sizeof(unw_context_t));
|
||||
ret = UNW_ESUCCESS;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int unw_step(unw_cursor_t* cp) {
|
||||
static dwarf_regs state = { 0 };
|
||||
ExecutionFrame* frame = (ExecutionFrame*) cp;
|
||||
uint32_t size = 0;
|
||||
uint8_t* enc_values = NULL;
|
||||
|
||||
/* Start parsing the .eh_frame_hdr section. */
|
||||
fde_header* header = (fde_header*) EH_FRAME_HDR_ADDR;
|
||||
if (header->version != 1) {
|
||||
goto badversion;
|
||||
}
|
||||
|
||||
/* Make enc_values point to the end of the structure, where the encoded
|
||||
* values start. */
|
||||
enc_values = (uint8_t*) (header + 1);
|
||||
|
||||
/* Retrieve the encoded value eh_frame_ptr. Get the size of the data also. */
|
||||
const uint32_t eh_frame_ptr = esp_eh_frame_get_encoded(enc_values, header->eh_frame_ptr_enc, &size);
|
||||
assert(eh_frame_ptr == (uint32_t) EH_FRAME_ADDR);
|
||||
enc_values += size;
|
||||
|
||||
/* Same for the number of entries in the sorted table. */
|
||||
const uint32_t fde_count = esp_eh_frame_get_encoded(enc_values, header->fde_count_enc, &size);
|
||||
enc_values += size;
|
||||
|
||||
/* enc_values points now at the beginning of the sorted table. */
|
||||
/* Only support 4-byte entries. */
|
||||
const uint32_t table_enc = header->table_enc;
|
||||
if ( ((table_enc >> 4) != 0x3) && ((table_enc >> 4) != 0xB) ) {
|
||||
goto badversion;
|
||||
}
|
||||
|
||||
const table_entry* sorted_table = (const table_entry*) enc_values;
|
||||
|
||||
const table_entry* from_fun = esp_eh_frame_find_entry(sorted_table, fde_count,
|
||||
table_enc, EXECUTION_FRAME_PC(*frame));
|
||||
|
||||
/* Get absolute address of FDE entry describing the function where PC left of. */
|
||||
uint32_t* fde = NULL;
|
||||
if (from_fun != NULL) {
|
||||
fde = esp_eh_frame_decode_address(&from_fun->fde_addr, table_enc);
|
||||
}
|
||||
|
||||
if (esp_eh_frame_missing_info(fde, EXECUTION_FRAME_PC(*frame))) {
|
||||
goto missinginfo;
|
||||
}
|
||||
|
||||
const uint32_t prev_sp = EXECUTION_FRAME_SP(*frame);
|
||||
|
||||
/* Retrieve the return address of the frame. The frame's registers will be modified.
|
||||
* The frame we get then is the caller's one. */
|
||||
uint32_t ra = esp_eh_frame_restore_caller_state(fde, frame, &state);
|
||||
|
||||
/* End of backtrace is reached if the stack and the PC don't change anymore. */
|
||||
if ((EXECUTION_FRAME_SP(*frame) == prev_sp) && (EXECUTION_FRAME_PC(*frame) == ra)) {
|
||||
goto stopunwind;
|
||||
}
|
||||
|
||||
/* Go back to the caller: update stack pointer and program counter. */
|
||||
EXECUTION_FRAME_PC(*frame) = ra;
|
||||
|
||||
return 1;
|
||||
badversion:
|
||||
return -UNW_EBADVERSION;
|
||||
missinginfo:
|
||||
return -UNW_ENOINFO;
|
||||
stopunwind:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unw_get_reg(unw_cursor_t* cp, unw_regnum_t reg, unw_word_t* valp) {
|
||||
if (cp == NULL || valp == NULL) {
|
||||
goto invalid;
|
||||
}
|
||||
if (reg >= EXECUTION_FRAME_MAX_REGS) {
|
||||
goto badreg;
|
||||
}
|
||||
|
||||
*valp = EXECUTION_FRAME_REG(cp, reg);
|
||||
return UNW_ESUCCESS;
|
||||
invalid:
|
||||
return -UNW_EUNSPEC;
|
||||
badreg:
|
||||
return -UNW_EBADREG;
|
||||
}
|
||||
|
||||
int unw_set_reg(unw_cursor_t* cp, unw_regnum_t reg, unw_word_t val) {
|
||||
if (cp == NULL) {
|
||||
goto invalid;
|
||||
}
|
||||
if (reg >= EXECUTION_FRAME_MAX_REGS) {
|
||||
goto badreg;
|
||||
}
|
||||
|
||||
EXECUTION_FRAME_REG(cp, reg) = val;
|
||||
return UNW_ESUCCESS;
|
||||
invalid:
|
||||
return -UNW_EUNSPEC;
|
||||
badreg:
|
||||
return -UNW_EBADREG;
|
||||
}
|
||||
|
||||
#endif //ESP_SYSTEM_USE_EH_FRAME
|
||||
|
@ -24,4 +24,5 @@ void esp_eh_frame_print_backtrace(const void *frame_or);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif // EH_FRAME_PARSER_H
|
||||
|
135
components/esp_system/include/libunwind.h
Normal file
135
components/esp_system/include/libunwind.h
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef LIBUNWIND_H
|
||||
#define LIBUNWIND_H
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
#include "libunwind-riscv.h"
|
||||
#elif CONFIG_IDF_TARGET_X86
|
||||
#include "libunwind-x86.h"
|
||||
#else
|
||||
/* This header must be a standalone one, so, it shall not trigger an error when
|
||||
* pre-processed without including any of the architecture header above.
|
||||
* The implementation can trigger a compile error if UNW_UNKNOWN_TARGET
|
||||
* macro is defined. */
|
||||
#define UNW_UNKNOWN_TARGET 1
|
||||
typedef void* ExecutionFrame;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Error codes returned by the functions defined below */
|
||||
#define UNW_ESUCCESS 0
|
||||
#define UNW_EUNSPEC 1 /* General failure */
|
||||
#define UNW_EBADREG 3 /* Register given is wrong */
|
||||
#define UNW_ESTOPUNWIND 5
|
||||
#define UNW_EINVAL 8 /* Bad parameter or unimplemented operation */
|
||||
#define UNW_EBADVERSION 9
|
||||
#define UNW_ENOINFO 10
|
||||
|
||||
/* A libunwind context is the equivalent of an ESP-IDF ExecutionFrame */
|
||||
typedef ExecutionFrame unw_context_t;
|
||||
|
||||
/* A register number is an unsigned word in our case */
|
||||
typedef uint32_t unw_regnum_t;
|
||||
|
||||
/* In our current implementation, a cursor is the same as a context */
|
||||
typedef unw_context_t unw_cursor_t;
|
||||
|
||||
/* long should represent the size of a CPU register */
|
||||
typedef unsigned long unw_word_t;
|
||||
|
||||
/* At the moment, we don't support the operations using the following types,
|
||||
* so just set them to void* */
|
||||
typedef void* unw_addr_space_t;
|
||||
typedef void* unw_fpreg_t;
|
||||
|
||||
/**
|
||||
* @brief Get the current CPU context.
|
||||
*
|
||||
* @param[out] ctx Pointer to `unw_context_t` structure. It must not be NULL
|
||||
* as it will be filled with the CPU registers value
|
||||
*
|
||||
* @return UNW_ESUCCESS on success, -UNW_EUNSPEC if ctx is NULL
|
||||
*
|
||||
* @note This function MUST be inlined. Marking it as "static inline" or
|
||||
* __attribute__((always_inline)) does not guarantee that it will inlined by
|
||||
* the compiler for all the architectures. Thus, define this function as a macro.
|
||||
* @note If the caller of this function returns, all the pointers, contexts, cursors
|
||||
* generated out of the initial returned context shall be considered invalid and
|
||||
* thus, must **not** be used.
|
||||
*/
|
||||
#define unw_getcontext(ctx) ({ int retval; \
|
||||
if (ctx == NULL) { \
|
||||
retval = -UNW_EUNSPEC; \
|
||||
} else { \
|
||||
UNW_GET_CONTEXT(ctx); \
|
||||
retval = UNW_ESUCCESS; \
|
||||
} \
|
||||
retval; \
|
||||
})
|
||||
|
||||
/**
|
||||
* @brief Initialize a cursor on a local context. Multiple cursor can be initialized on
|
||||
* a given CPU context, they can then be manipulated independently.
|
||||
*
|
||||
* @param[out] c Pointer on cursor to be returned. Must not be NULL
|
||||
* @param[in] ctx Pointer on the context returned by the function `unw_getcontext`
|
||||
*
|
||||
* @return UNW_ESUCCESS on success, -UNW_EUNSPEC if one of the parameter is NULL.
|
||||
*/
|
||||
int unw_init_local(unw_cursor_t* c, unw_context_t* ctx);
|
||||
|
||||
/**
|
||||
* @brief Perform a step "up" on the given cursor. After calling this function, the
|
||||
* cursor will point to the caller's CPU context. Thus, it is then possible
|
||||
* to retrieve the caller's address by getting the PC register out of the cursor.
|
||||
* Check `unw_get_reg` function for this.
|
||||
*
|
||||
* @param[in] cp Current cursor
|
||||
*
|
||||
* @returns 0 if the previous frame was the last one
|
||||
* @returns Positive value on success
|
||||
* @returns -UNW_EBADVERSION if the DWARF information's version is not compatible with the eh_frame_parser implementation
|
||||
* @returns -UNW_ENOINFO if the caller information are not present in the binary. (if the caller is in ROM for example)
|
||||
* @returns -UNW_ESTOPUNWIND if unwinding is terminated
|
||||
*/
|
||||
int unw_step(unw_cursor_t* cp);
|
||||
|
||||
/**
|
||||
* @brief Get the value of a CPU register from a given cursor.
|
||||
*
|
||||
* @param[in] cp Pointer to the cursor
|
||||
* @param reg Register number to retrieve the value of
|
||||
* @param[out] valp Pointer that will be filled with the register value
|
||||
*
|
||||
* @returns UNW_ESUCCESS on success
|
||||
* @returns -UNW_EUNSPEC if any pointer passed is NULL
|
||||
* @returns -UNW_EBADREG if the register number is invalid
|
||||
*/
|
||||
int unw_get_reg(unw_cursor_t* cp, unw_regnum_t reg, unw_word_t* valp);
|
||||
|
||||
/**
|
||||
* @brief Set the value of a CPU register in a given cursor.
|
||||
*
|
||||
* @param[in]cp Pointer to the cursor
|
||||
* @param reg Register number to set the value of
|
||||
* @param val New register value
|
||||
*
|
||||
* @returns UNW_ESUCCESS on success
|
||||
* @returns -UNW_EUNSPEC if the pointer passed is NULL
|
||||
* @returns -UNW_EBADREG if the register number is invalid
|
||||
*/
|
||||
int unw_set_reg(unw_cursor_t* cp, unw_regnum_t reg, unw_word_t val);
|
||||
|
||||
#endif // LIBUNWIND_H
|
@ -1,64 +0,0 @@
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file DWARF Exception Frames parser header
|
||||
*
|
||||
* This file describes the frame types for RISC-V, required for
|
||||
* parsing `eh_frame` and `eh_frame_hdr`.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "riscv/rvruntime-frames.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Define the Executionframe as RvExcFrame for this implementation.
|
||||
*/
|
||||
typedef RvExcFrame ExecutionFrame;
|
||||
|
||||
/**
|
||||
* @brief Number of registers in the ExecutionFrame structure.
|
||||
*
|
||||
* This will be used to define and initialize the DWARF machine state.
|
||||
* In practice, we only have 16 registers that are callee saved, thus, we could
|
||||
* only save them and ignore the rest. However, code to calculate mapping of
|
||||
* CPU registers to DWARF registers would take more than the 16 registers we
|
||||
* would save... so save all registers.
|
||||
*/
|
||||
#define EXECUTION_FRAME_MAX_REGS (32)
|
||||
|
||||
/**
|
||||
* @brief Reference the PC register of the execution frame.
|
||||
*/
|
||||
#define EXECUTION_FRAME_PC(frame) ((frame).mepc)
|
||||
|
||||
/**
|
||||
* @brief Reference the SP register of the execution frame.
|
||||
*/
|
||||
#define EXECUTION_FRAME_SP(frame) ((frame).sp)
|
||||
|
||||
/**
|
||||
* @brief Index of SP register in the execution frame.
|
||||
*/
|
||||
#define EXECUTION_FRAME_SP_REG (offsetof(RvExcFrame, sp)/sizeof(uint32_t))
|
||||
|
||||
/**
|
||||
* @brief Get register i of the execution frame.
|
||||
*/
|
||||
#define EXECUTION_FRAME_REG(frame, i) (((uint32_t*) (frame))[(i)])
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// #endif // _EH_FRAME_PARSER_IMPL_H
|
213
components/esp_system/port/include/riscv/libunwind-riscv.h
Normal file
213
components/esp_system/port/include/riscv/libunwind-riscv.h
Normal file
@ -0,0 +1,213 @@
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file describes the frame types for RISC-V, required for
|
||||
* parsing `eh_frame` and `eh_frame_hdr`, and more generally libunwind.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "esp_attr.h"
|
||||
#include "riscv/rvruntime-frames.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Define the size of a CPU register.
|
||||
*/
|
||||
#define ARCH_WORD_SIZE (sizeof(long))
|
||||
|
||||
/**
|
||||
* @brief Retrive the index of a field inside a structure. All the fields
|
||||
* must have a word size.
|
||||
*/
|
||||
#define indexof(structure,field) (offsetof(structure, field) / ARCH_WORD_SIZE)
|
||||
|
||||
/**
|
||||
* @brief Define the Executionframe as RvExcFrame for this implementation.
|
||||
*/
|
||||
typedef RvExcFrame ExecutionFrame;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of all the registers for RISC-V architecture
|
||||
*/
|
||||
typedef enum {
|
||||
UNW_RISCV_PC = indexof(ExecutionFrame, mepc),
|
||||
UNW_RISCV_RA = indexof(ExecutionFrame, ra),
|
||||
UNW_RISCV_SP = indexof(ExecutionFrame, sp),
|
||||
UNW_RISCV_GP = indexof(ExecutionFrame, gp),
|
||||
UNW_RISCV_TP = indexof(ExecutionFrame, tp),
|
||||
UNW_RISCV_T0 = indexof(ExecutionFrame, t0),
|
||||
UNW_RISCV_T1 = indexof(ExecutionFrame, t1),
|
||||
UNW_RISCV_T2 = indexof(ExecutionFrame, t2),
|
||||
UNW_RISCV_S0 = indexof(ExecutionFrame, s0),
|
||||
UNW_RISCV_S1 = indexof(ExecutionFrame, s1),
|
||||
UNW_RISCV_A0 = indexof(ExecutionFrame, a0),
|
||||
UNW_RISCV_A1 = indexof(ExecutionFrame, a1),
|
||||
UNW_RISCV_A2 = indexof(ExecutionFrame, a2),
|
||||
UNW_RISCV_A3 = indexof(ExecutionFrame, a3),
|
||||
UNW_RISCV_A4 = indexof(ExecutionFrame, a4),
|
||||
UNW_RISCV_A5 = indexof(ExecutionFrame, a5),
|
||||
UNW_RISCV_A6 = indexof(ExecutionFrame, a6),
|
||||
UNW_RISCV_A7 = indexof(ExecutionFrame, a7),
|
||||
UNW_RISCV_S2 = indexof(ExecutionFrame, s2),
|
||||
UNW_RISCV_S3 = indexof(ExecutionFrame, s3),
|
||||
UNW_RISCV_S4 = indexof(ExecutionFrame, s4),
|
||||
UNW_RISCV_S5 = indexof(ExecutionFrame, s5),
|
||||
UNW_RISCV_S6 = indexof(ExecutionFrame, s6),
|
||||
UNW_RISCV_S7 = indexof(ExecutionFrame, s7),
|
||||
UNW_RISCV_S8 = indexof(ExecutionFrame, s8),
|
||||
UNW_RISCV_S9 = indexof(ExecutionFrame, s9),
|
||||
UNW_RISCV_S10 = indexof(ExecutionFrame, s10),
|
||||
UNW_RISCV_S11 = indexof(ExecutionFrame, s11),
|
||||
UNW_RISCV_T3 = indexof(ExecutionFrame, t3),
|
||||
UNW_RISCV_T4 = indexof(ExecutionFrame, t4),
|
||||
UNW_RISCV_T5 = indexof(ExecutionFrame, t5),
|
||||
UNW_RISCV_T6 = indexof(ExecutionFrame, t6),
|
||||
UNW_RISCV_MSTATUS = indexof(ExecutionFrame, mstatus),
|
||||
UNW_RISCV_MTVEC = indexof(ExecutionFrame, mtvec),
|
||||
UNW_RISCV_MCAUSE = indexof(ExecutionFrame, mcause),
|
||||
UNW_RISCV_MTVAL = indexof(ExecutionFrame, mtval),
|
||||
UNW_RISCV_MHARTID = indexof(ExecutionFrame, mhartid),
|
||||
} riscv_regnum_t;
|
||||
|
||||
/**
|
||||
* @brief Number of registers in the ExecutionFrame structure.
|
||||
*
|
||||
* This will be used to define and initialize the DWARF machine state.
|
||||
* In practice, we only have 16 registers that are callee saved, thus, we could
|
||||
* only save them and ignore the rest. However, code to calculate mapping of
|
||||
* CPU registers to DWARF registers would take more than the 16 registers we
|
||||
* would save... so save all registers.
|
||||
*/
|
||||
#define EXECUTION_FRAME_MAX_REGS (32)
|
||||
|
||||
/**
|
||||
* @brief Reference the PC register of the execution frame.
|
||||
*/
|
||||
#define EXECUTION_FRAME_PC(frame) ((frame).mepc)
|
||||
|
||||
/**
|
||||
* @brief Reference the SP register of the execution frame.
|
||||
*/
|
||||
#define EXECUTION_FRAME_SP(frame) ((frame).sp)
|
||||
|
||||
/**
|
||||
* @brief Index of SP register in the execution frame.
|
||||
*/
|
||||
#define EXECUTION_FRAME_SP_REG (indexof(RvExcFrame, sp))
|
||||
|
||||
/**
|
||||
* @brief Get register i of the execution frame.
|
||||
*/
|
||||
#define EXECUTION_FRAME_REG(frame, i) (((uint32_t*) (frame))[(i)])
|
||||
|
||||
/**
|
||||
* @brief Get the current context
|
||||
*/
|
||||
FORCE_INLINE_ATTR void UNW_GET_CONTEXT(ExecutionFrame* frame) {
|
||||
__asm__ __volatile__("sw t0, %1(%0)\n"
|
||||
"auipc t0, 0\n"
|
||||
"sw t0, %2(%0)\n"
|
||||
"sw ra, %3(%0)\n"
|
||||
"sw sp, %4(%0)\n"
|
||||
"sw gp, %5(%0)\n"
|
||||
"sw tp, %6(%0)\n"
|
||||
"sw t1, %7(%0)\n"
|
||||
"sw t2, %8(%0)\n"
|
||||
"sw s0, %9(%0)\n"
|
||||
"sw s1, %10(%0)\n"
|
||||
"sw a0, %11(%0)\n"
|
||||
"sw a1, %12(%0)\n"
|
||||
"sw a2, %13(%0)\n"
|
||||
"sw a3, %14(%0)\n"
|
||||
"sw a4, %15(%0)\n"
|
||||
"sw a5, %16(%0)\n"
|
||||
"sw a6, %17(%0)\n"
|
||||
"sw a7, %18(%0)\n"
|
||||
"sw s2, %19(%0)\n"
|
||||
"sw s3, %20(%0)\n"
|
||||
"sw s4, %21(%0)\n"
|
||||
"sw s5, %22(%0)\n"
|
||||
"sw s6, %23(%0)\n"
|
||||
"sw s7, %24(%0)\n"
|
||||
"sw s8, %25(%0)\n"
|
||||
"sw s9, %26(%0)\n"
|
||||
"sw s10, %27(%0)\n"
|
||||
"sw s11, %28(%0)\n"
|
||||
"sw t3, %29(%0)\n"
|
||||
:
|
||||
: "r" (frame),
|
||||
"i" (UNW_RISCV_T0 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_PC * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_RA * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_SP * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_GP * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_TP * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_T1 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_T2 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_S0 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_S1 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_A0 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_A1 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_A2 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_A3 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_A4 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_A5 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_A6 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_A7 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_S2 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_S3 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_S4 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_S5 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_S6 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_S7 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_S8 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_S9 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_S10 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_S11 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_T3 * ARCH_WORD_SIZE)
|
||||
);
|
||||
/* GCC doesn't allow us to have more than 30 operands in a single
|
||||
* __asm__ __volatile__ definition, so we have to split it into 2 */
|
||||
__asm__ __volatile__("sw t4, %1(%0)\n"
|
||||
"sw t5, %2(%0)\n"
|
||||
"sw t6, %3(%0)\n"
|
||||
"csrr t0, mstatus\n"
|
||||
"sw t0, %4(%0)\n"
|
||||
"csrr t0, mtvec\n"
|
||||
"sw t0, %5(%0)\n"
|
||||
"csrr t0, mcause\n"
|
||||
"sw t0, %6(%0)\n"
|
||||
"csrr t0, mtval\n"
|
||||
"sw t0, %7(%0)\n"
|
||||
"csrr t0, mhartid\n"
|
||||
"sw t0, %8(%0)\n"
|
||||
/* We have to restore t0 as it may be in use by the function that makes the use of this assembly snippet */
|
||||
"lw t0, %9(%0)\n"
|
||||
:
|
||||
: "r" (frame),
|
||||
"i" (UNW_RISCV_T4 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_T5 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_T6 * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_MSTATUS * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_MTVEC * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_MCAUSE * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_MTVAL * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_MHARTID * ARCH_WORD_SIZE),
|
||||
"i" (UNW_RISCV_T0 * ARCH_WORD_SIZE)
|
||||
);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
146
components/esp_system/port/include/x86/libunwind-x86.h
Normal file
146
components/esp_system/port/include/x86/libunwind-x86.h
Normal file
@ -0,0 +1,146 @@
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Define the size of a CPU register.
|
||||
*/
|
||||
#define ARCH_WORD_SIZE (sizeof(long))
|
||||
|
||||
/**
|
||||
* @brief Retrieve the index of a field inside a structure. All the fields
|
||||
* must have a word size.
|
||||
*/
|
||||
#define indexof(structure,field) (offsetof(structure, field) / ARCH_WORD_SIZE)
|
||||
|
||||
/**
|
||||
* @brief Number of registers in the ExecutionFrame structure.
|
||||
*/
|
||||
#define EXECUTION_FRAME_MAX_REGS (11)
|
||||
|
||||
/**
|
||||
* @brief Definition of the x86 DWARF registers set.
|
||||
* The following registers order has been taken from GCC's `i386.c` file:
|
||||
*/
|
||||
typedef struct x86ExcFrame
|
||||
{
|
||||
union {
|
||||
struct {
|
||||
uint32_t eax;
|
||||
uint32_t ecx;
|
||||
uint32_t edx;
|
||||
uint32_t ebx;
|
||||
uint32_t esp;
|
||||
uint32_t ebp;
|
||||
uint32_t esi;
|
||||
uint32_t edi;
|
||||
uint32_t eip;
|
||||
uint32_t eflags;
|
||||
uint32_t trapno;
|
||||
};
|
||||
uint32_t registers[EXECUTION_FRAME_MAX_REGS];
|
||||
};
|
||||
} x86ExcFrame;
|
||||
|
||||
/**
|
||||
* @brief Define the Executionframe as RvExcFrame for this implementation.
|
||||
*/
|
||||
typedef x86ExcFrame ExecutionFrame;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of the registers for x86 (32-bit) architecture
|
||||
*/
|
||||
typedef enum {
|
||||
UNW_X86_EAX = indexof(ExecutionFrame, eax),
|
||||
UNW_X86_ECX = indexof(ExecutionFrame, ecx),
|
||||
UNW_X86_EDX = indexof(ExecutionFrame, edx),
|
||||
UNW_X86_EBX = indexof(ExecutionFrame, ebx),
|
||||
UNW_X86_ESP = indexof(ExecutionFrame, esp),
|
||||
UNW_X86_EBP = indexof(ExecutionFrame, ebp),
|
||||
UNW_X86_ESI = indexof(ExecutionFrame, esi),
|
||||
UNW_X86_EDI = indexof(ExecutionFrame, edi),
|
||||
UNW_X86_EIP = indexof(ExecutionFrame, eip),
|
||||
UNW_X86_EFLAGS = indexof(ExecutionFrame, eflags),
|
||||
UNW_X86_TRAPNO = indexof(ExecutionFrame, trapno),
|
||||
} x86_regnum_t;
|
||||
|
||||
/**
|
||||
* @brief Reference the PC register of the execution frame
|
||||
*/
|
||||
#define EXECUTION_FRAME_PC(struct) ((struct).eip)
|
||||
|
||||
/**
|
||||
* @brief Reference the SP register of the execution frame
|
||||
*/
|
||||
#define EXECUTION_FRAME_SP(struct) ((struct).esp)
|
||||
|
||||
/**
|
||||
* @brief Index of SP register in the execution frame.
|
||||
*/
|
||||
#define EXECUTION_FRAME_SP_REG (indexof(x86ExcFrame, esp))
|
||||
|
||||
/**
|
||||
* @brief Get register i of the execution frame
|
||||
*/
|
||||
#define EXECUTION_FRAME_REG(frame, i) ((frame)->registers[(i)])
|
||||
|
||||
/**
|
||||
* @brief Get the current context
|
||||
*
|
||||
* @note For x86, this needs to be a macro, else, the compiler will not inline it,
|
||||
* even if we specify __attribute__((always_inline))
|
||||
*
|
||||
* @param ExecutionFrame* Pointer to the frame/context to fill
|
||||
*/
|
||||
#define UNW_GET_CONTEXT(frame) { \
|
||||
__asm__ __volatile__(".intel_syntax noprefix\n\t" \
|
||||
"mov DWORD PTR [%0 + %c1], eax\n\t" \
|
||||
"mov DWORD PTR [%0 + %c2], ecx\n\t" \
|
||||
"mov DWORD PTR [%0 + %c3], edx\n\t" \
|
||||
"mov DWORD PTR [%0 + %c4], ebx\n\t" \
|
||||
"mov DWORD PTR [%0 + %c5], esp\n\t" \
|
||||
"mov DWORD PTR [%0 + %c6], ebp\n\t" \
|
||||
"mov DWORD PTR [%0 + %c7], esi\n\t" \
|
||||
"mov DWORD PTR [%0 + %c8], edi\n\t" \
|
||||
"mov DWORD PTR [%0 + %c11], 0\n\t" \
|
||||
/* Special part for retrieving PC */ \
|
||||
"call __get_pc\n" \
|
||||
"__get_pc: pop ebx\n\t" \
|
||||
"mov DWORD PTR [%0 + %c9], ebx\n\t" \
|
||||
/* Same for the flags */ \
|
||||
"pushfd\n\t" \
|
||||
"pop ebx\n\t" \
|
||||
"mov DWORD PTR [%0 + %c10], ebx\n\t" \
|
||||
/* Restore EBX */ \
|
||||
"mov ebx, [%0 + %c4]\n\t" \
|
||||
".att_syntax prefix" \
|
||||
: \
|
||||
: "r" (frame), \
|
||||
"i" (UNW_X86_EAX * ARCH_WORD_SIZE), \
|
||||
"i" (UNW_X86_ECX * ARCH_WORD_SIZE), \
|
||||
"i" (UNW_X86_EDX * ARCH_WORD_SIZE), \
|
||||
"i" (UNW_X86_EBX * ARCH_WORD_SIZE), \
|
||||
"i" (UNW_X86_ESP * ARCH_WORD_SIZE), \
|
||||
"i" (UNW_X86_EBP * ARCH_WORD_SIZE), \
|
||||
"i" (UNW_X86_ESI * ARCH_WORD_SIZE), \
|
||||
"i" (UNW_X86_EDI * ARCH_WORD_SIZE), \
|
||||
"i" (UNW_X86_EIP * ARCH_WORD_SIZE), \
|
||||
"i" (UNW_X86_EFLAGS * ARCH_WORD_SIZE), \
|
||||
"i" (UNW_X86_TRAPNO * ARCH_WORD_SIZE) \
|
||||
); \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,20 +1,9 @@
|
||||
#
|
||||
# Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http:#www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
CC=gcc
|
||||
CFLAGS=-W -fasynchronous-unwind-tables -I. -I../include/ -std=c99 -g -DCONFIG_ESP_SYSTEM_USE_EH_FRAME -m32
|
||||
CFLAGS=-W -fasynchronous-unwind-tables -I. -I../port/include/x86/ -I../include/ -I../include/esp_private -std=c99 -g -m32
|
||||
LDFLAGS=-Wl,--eh-frame-hdr -m32 -g -Tlinker.ld -no-pie
|
||||
OBJECTS=objs/eh_frame_parser.o objs/main.o
|
||||
HEADERS=eh_frame_parser_impl.h
|
||||
|
@ -1,83 +0,0 @@
|
||||
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @file DWARF Exception Frames parser header
|
||||
*
|
||||
* This file describes the frame types for x86, required for
|
||||
* parsing `eh_frame` and `eh_frame_hdr`.
|
||||
*/
|
||||
|
||||
#ifndef EH_FRAME_PARSER_IMPL_H
|
||||
#define EH_FRAME_PARSER_IMPL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* @brief Number of registers in the ExecutionFrame structure.
|
||||
*/
|
||||
#define EXECUTION_FRAME_MAX_REGS (11)
|
||||
|
||||
/**
|
||||
* @brief Definition of the x86 DWARF tegisters set.
|
||||
* The following registers order has been taken from GCC's `i386.c` file:
|
||||
*/
|
||||
typedef struct x86ExcFrame
|
||||
{
|
||||
union {
|
||||
struct {
|
||||
uint32_t eax;
|
||||
uint32_t ecx;
|
||||
uint32_t edx;
|
||||
uint32_t ebx;
|
||||
uint32_t esp;
|
||||
uint32_t ebp;
|
||||
uint32_t esi;
|
||||
uint32_t edi;
|
||||
uint32_t eip;
|
||||
uint32_t eflags;
|
||||
uint32_t trapno;
|
||||
};
|
||||
uint32_t registers[EXECUTION_FRAME_MAX_REGS];
|
||||
};
|
||||
} x86ExcFrame;
|
||||
|
||||
/**
|
||||
* @brief Define the Executionframe as RvExcFrame for this implementation.
|
||||
*/
|
||||
typedef x86ExcFrame ExecutionFrame;
|
||||
|
||||
/**
|
||||
* @brief Reference the PC register of the execution frame
|
||||
*/
|
||||
#define EXECUTION_FRAME_PC(struct) ((struct).eip)
|
||||
|
||||
/**
|
||||
* @brief Reference the SP register of the execution frame
|
||||
*/
|
||||
#define EXECUTION_FRAME_SP(struct) ((struct).esp)
|
||||
|
||||
/**
|
||||
* @brief Index of SP register in the execution frame.
|
||||
*/
|
||||
#define EXECUTION_FRAME_SP_REG (offsetof(x86ExcFrame, esp)/sizeof(uint32_t))
|
||||
|
||||
/**
|
||||
* @brief Get register i of the execution frame
|
||||
*/
|
||||
#define EXECUTION_FRAME_REG(frame, i) ((frame)->registers[(i)])
|
||||
|
||||
#endif // _EH_FRAME_PARSER_IMPL_H
|
@ -5,13 +5,6 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file DWARF Exception Frames parser header
|
||||
*
|
||||
* This file describes the frame types for x86, required for
|
||||
* parsing `eh_frame` and `eh_frame_hdr`.
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#define _DEFAULT_SOURCE
|
||||
#include <stdio.h>
|
||||
@ -22,8 +15,8 @@
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <ucontext.h>
|
||||
#include "../include/esp_private/eh_frame_parser.h"
|
||||
#include "eh_frame_parser_impl.h"
|
||||
#include "esp_private/eh_frame_parser.h"
|
||||
#include "libunwind.h"
|
||||
|
||||
/**
|
||||
* @brief Index of x86 registers in `greg_t` structure.
|
||||
@ -54,6 +47,36 @@
|
||||
*/
|
||||
#define NUMBER_OF_ITERATION (2 * NUMBER_TO_TEST + 2 + 1)
|
||||
|
||||
/**
|
||||
* @brief Macro for testing calls to libunwind when UNW_ESUCCESS must be returned.
|
||||
*/
|
||||
#define UNW_CHECK(call) do { if ((err = (call)) != UNW_ESUCCESS) { \
|
||||
printf("\e[31m\e[1mLibunwind error code %d on line %d\e[0m\r\n", err, __LINE__); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Macro for testing if the given condition is true. To be used with libunwind when
|
||||
* the result is not necessarily UNW_ESUCCESS.
|
||||
*/
|
||||
#define UNW_CHECK_TRUE(cond) do { \
|
||||
if (!(cond)) { \
|
||||
printf("\e[31m\e[1mLibunwind error on line %d\e[0m\r\n", __LINE__); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Macro for checking if a PC returned by libunwind is part of the given function
|
||||
*/
|
||||
#define UNW_CHECK_PC(pc, funname) do { \
|
||||
if (!is_pc_in_function((pc), (funname))) { \
|
||||
printf("\e[31m\e[1mPC %04lx should have been of function %s\e[0m\r\n", (pc), (funname)); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief Define a simple linked list type and initialize one.
|
||||
*/
|
||||
@ -70,6 +93,10 @@ static struct list_t head = { 0 };
|
||||
bool is_odd(uint32_t n);
|
||||
bool is_even(uint32_t n);
|
||||
void browse_list(struct list_t* l);
|
||||
int analyse_callstack();
|
||||
int inner_function1(void);
|
||||
int inner_function2(void);
|
||||
void test1(void);
|
||||
|
||||
/**
|
||||
* @brief Structure defining a function of our program.
|
||||
@ -100,7 +127,27 @@ struct functions_info funs[] = {
|
||||
.name = "is_even",
|
||||
.start = (uintptr_t) &is_even,
|
||||
.end = 0
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "analyse_callstack",
|
||||
.start = (uintptr_t) &analyse_callstack,
|
||||
.end = 0
|
||||
},
|
||||
{
|
||||
.name = "inner_function1",
|
||||
.start = (uintptr_t) &inner_function1,
|
||||
.end = 0
|
||||
},
|
||||
{
|
||||
.name = "inner_function2",
|
||||
.start = (uintptr_t) &inner_function2,
|
||||
.end = 0
|
||||
},
|
||||
{
|
||||
.name = "test1",
|
||||
.start = (uintptr_t) &test1,
|
||||
.end = 0
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@ -167,7 +214,7 @@ void esp_eh_frame_generated_step(uint32_t pc, uint32_t sp) {
|
||||
/**
|
||||
* @brief Handler called when SIGSEV signal is sent to the program.
|
||||
*
|
||||
* @param signal Signal received byt the program. Shall be SIGSEGV.
|
||||
* @param signal Signal received by the program. Shall be SIGSEGV.
|
||||
* @param info Structure containing info about the error itself. Ignored.
|
||||
* @param ucontext Context of the program when the error occurred. This
|
||||
* is used to retrieve the CPU registers value.
|
||||
@ -269,7 +316,7 @@ bool is_odd(uint32_t n) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initiliaze the global `funs` array.
|
||||
* @brief Initialize the global `funs` array.
|
||||
*/
|
||||
static inline void initialize_functions_info(void)
|
||||
{
|
||||
@ -278,9 +325,13 @@ static inline void initialize_functions_info(void)
|
||||
* with the following instructions:
|
||||
* leave (0xc9)
|
||||
* ret (0xc3)
|
||||
* or
|
||||
* pop ebp (0x5d)
|
||||
* ret (0xc3)
|
||||
* Thus, we will look for these instructions. */
|
||||
uint8_t* instructions = (uint8_t*) funs[i].start;
|
||||
while (instructions[0] != 0xc9 || instructions[1] != 0xc3)
|
||||
while ((instructions[0] != 0xc9 || instructions[1] != 0xc3) &&
|
||||
(instructions[0] != 0x5d || instructions[1] != 0xc3) )
|
||||
instructions++;
|
||||
instructions += 1;
|
||||
funs[i].end = (uintptr_t) instructions;
|
||||
@ -288,10 +339,9 @@ static inline void initialize_functions_info(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the previous functions to create a complex call stack and fail.
|
||||
* Test the eh_frame_parser for backtracing
|
||||
*/
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
void test2(void) {
|
||||
/* Initialize the structure holding information about the signal to override. */
|
||||
struct sigaction sig = {
|
||||
.sa_mask = 0,
|
||||
@ -300,18 +350,68 @@ int main (int argc, char** argv)
|
||||
.sa_sigaction = signal_handler
|
||||
};
|
||||
|
||||
/* Look for the functions end functions. */
|
||||
initialize_functions_info();
|
||||
|
||||
/* Override default SIGSEV signal callback. */
|
||||
int res = sigaction(SIGSEGV, &sig, NULL);
|
||||
if (res) {
|
||||
perror("Could not override SIGSEV signal");
|
||||
return 1;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Trigger the segmentation fault with a complex backtrace. */
|
||||
is_even(NUMBER_TO_TEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the libunwind implementation in ESP-IDF
|
||||
* Let's create some nested function calls to make unwinding more interesting.
|
||||
* Important: the stack must still be alive when analyzing it, thus it must be done
|
||||
* within the nested functions.
|
||||
*/
|
||||
int analyse_callstack() {
|
||||
unw_context_t ucp = { 0 };
|
||||
unw_cursor_t cur = { 0 };
|
||||
unw_word_t pc = 0;
|
||||
int err = UNW_ESUCCESS;
|
||||
|
||||
UNW_CHECK(unw_getcontext(&ucp));
|
||||
UNW_CHECK(unw_init_local(&cur, &ucp));
|
||||
UNW_CHECK(unw_get_reg(&cur, UNW_X86_EIP, &pc));
|
||||
/* This PC must be inside analyse_callstack */
|
||||
UNW_CHECK_PC(pc, "analyse_callstack");
|
||||
/* unw_step returns a positive value on success */
|
||||
UNW_CHECK_TRUE(unw_step(&cur) > 0);
|
||||
UNW_CHECK(unw_get_reg(&cur, UNW_X86_EIP, &pc));
|
||||
UNW_CHECK_PC(pc, "inner_function2");
|
||||
UNW_CHECK_TRUE(unw_step(&cur) > 0);
|
||||
UNW_CHECK(unw_get_reg(&cur, UNW_X86_EIP, &pc));
|
||||
UNW_CHECK_PC(pc, "inner_function1");
|
||||
/* unw_step returns if the frame is last one */
|
||||
UNW_CHECK_TRUE(unw_step(&cur) >= 0);
|
||||
UNW_CHECK(unw_get_reg(&cur, UNW_X86_EIP, &pc));
|
||||
UNW_CHECK_PC(pc, "test1");
|
||||
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
|
||||
int __attribute__((noinline)) inner_function2(void) {
|
||||
return analyse_callstack();
|
||||
}
|
||||
|
||||
int __attribute__((noinline)) inner_function1(void) {
|
||||
return inner_function2();
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) test1() {
|
||||
(void) inner_function1();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the previous tests within the main. If the first test fails, it will exit by itself.
|
||||
*/
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
initialize_functions_info();
|
||||
test1();
|
||||
test2();
|
||||
return 0;
|
||||
}
|
||||
|
12
components/esp_system/test_eh_frame_parser/sdkconfig.h
Normal file
12
components/esp_system/test_eh_frame_parser/sdkconfig.h
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef SDKCONFIG_H
|
||||
#define SDKCONFIG_H
|
||||
|
||||
#define CONFIG_ESP_SYSTEM_USE_EH_FRAME 1
|
||||
#define CONFIG_IDF_TARGET_X86 1
|
||||
|
||||
#endif // SDKCONFIG_H
|
Loading…
Reference in New Issue
Block a user