espsystem: add support for RISC-V panic backtrace

Add .eh_frame and .eh_frame_hdr sections to the binary (can be
enabled/disabled within menuconfig). These sections are parsed
when a panic occurs. Their DWARF instructions are decoded and
executed at runtime, to retrieve the whole backtrace. This
parser has been tested on both RISC-V and x86 architectures.

This feature needs esptool's merge adjacent ELF sections feature.
pull/7307/head
Omar Chebib 2021-01-14 12:09:40 +08:00
rodzic 34c16dba93
commit b967dc0dbf
17 zmienionych plików z 1560 dodań i 4 usunięć

Wyświetl plik

@ -15,7 +15,6 @@ unset(link_options)
# Add the following build specifications here, since these seem to be dependent
# on config values on the root Kconfig.
if(NOT BOOTLOADER_BUILD)
if(CONFIG_COMPILER_OPTIMIZATION_SIZE)
@ -108,6 +107,11 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
)
endif()
if(CONFIG_ESP_SYSTEM_USE_EH_FRAME)
list(APPEND compile_options "-fasynchronous-unwind-tables")
list(APPEND link_options "-Wl,--eh-frame-hdr")
endif()
list(APPEND link_options "-fno-lto")
# Placing jump tables in flash would cause issues with code that required

Wyświetl plik

@ -831,7 +831,11 @@ void bootloader_reset(void)
void bootloader_atexit(void)
{
#ifdef BOOTLOADER_BUILD
bootloader_console_deinit();
#else
abort();
#endif
}
esp_err_t bootloader_sha256_hex_to_str(char *out_str, const uint8_t *in_array_hex, size_t len)

Wyświetl plik

@ -116,3 +116,8 @@ REGION_ALIAS("rtc_data_location", rtc_iram_seg );
ASSERT(_flash_rodata_dummy_start == ORIGIN(default_rodata_seg),
".flash_rodata_dummy section must be placed at the beginning of the rodata segment.")
#endif
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
ASSERT ((_eh_frame_end > _eh_frame), "Error: eh_frame size is null!");
ASSERT ((_eh_frame_hdr_end > _eh_frame_hdr), "Error: eh_frame_hdr size is null!");
#endif

Wyświetl plik

@ -289,9 +289,6 @@ SECTIONS
*(.gcc_except_table .gcc_except_table.*)
*(.gnu.linkonce.e.*)
*(.gnu.version_r)
. = (. + 3) & ~ 3;
__eh_frame = ABSOLUTE(.);
KEEP(*(.eh_frame))
. = (. + 7) & ~ 3;
/*
* C++ constructor and destructor tables
@ -345,6 +342,20 @@ SECTIONS
. = ALIGN(4);
} > default_rodata_seg
.eh_frame :
{
_eh_frame = ABSOLUTE(.);
KEEP (*(.eh_frame))
_eh_frame_end = ABSOLUTE(.);
} > drom0_0_seg
.eh_frame_hdr :
{
_eh_frame_hdr = ABSOLUTE(.);
KEEP (*(.eh_frame_hdr))
_eh_frame_hdr_end = ABSOLUTE(.);
} > drom0_0_seg
.flash.rodata_noload (NOLOAD) :
{
. = ALIGN (4);

Wyświetl plik

@ -93,6 +93,16 @@ menu "ESP System Settings"
heap initialization order by early startup services and scheduler related code. Speed
wise RTC fast memory operates on APB clock and hence does not have much performance impact.
config ESP_SYSTEM_USE_EH_FRAME
bool "Generate and use eh_frame for backtracing"
default n
depends on IDF_TARGET_ARCH_RISCV
help
Generate DWARF information in the resulting binary to perform backtracing when panics occur. Activating
this option will activate asynchronous frame unwinding and generation of both .eh_frame and .eh_frame_hdr
sections, resulting in a bigger binary size (20% to 100% larger). This option shall be not be used for
production.
menu "Memory protection"
config ESP_SYSTEM_MEMPROT_FEATURE

Wyświetl plik

@ -0,0 +1,895 @@
// 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
*
* This file performs parsing and execution of DWARF except frames described in
* section `.eh_frame` and `.eh_frame_hdr`. This is currently used on RISC-V
* boards to implement a complete backtracing when a panic occurs.
*
* More information about the sections structure and DWARF instructions can be
* found in the official documentation:
* http://dwarfstd.org/Download.php
*/
#include "port/eh_frame_parser.h"
#include "port/eh_frame_parser_impl.h"
#include "esp_private/panic_internal.h"
#include <string.h>
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
/**
* @brief Dimension of an array (number of elements)
*/
#ifndef DIM
#define DIM(array) (sizeof(array)/sizeof(*array))
#endif
/**
* @brief DWARF Exception Header Encoding
* This is used to know how the data in .eh_frame and .eh_frame_hdr sections
* are encoded.
*/
/* DWARF Exception Exception Header value format. */
#define DW_EH_PE_omit 0xff /*!< No value is present */
#define DW_EH_PE_uleb128 0x01 /*!< Unsigned value encoded in LEB128 (Little Endian Base 128). */
#define DW_EH_PE_udata2 0x02 /*!< Unsigned 16-bit value. */
#define DW_EH_PE_udata4 0x03 /*!< Unsigned 32-bit value. */
#define DW_EH_PE_udata8 0x04 /*!< Unsigned 64-bit value. */
#define DW_EH_PE_sleb128 0x09 /*!< Signed value encoded in LEB128 (Little Endian Base 128). */
#define DW_EH_PE_sdata2 0x0A /*!< Signed 16-bit value. */
#define DW_EH_PE_sdata4 0x0B /*!< Signed 32-bit value. */
#define DW_EH_PE_sdata8 0x0C /*!< Signed 64-bit value. */
/* DWARF Exception Exception Header value application.
* These values are in fact represented in the high nibble of a given data.
* For example:
* 0x3A describes the values as signed 16-bit offsets relative to .eh_frame_hdr section.
* 0x11 describes the values as unsigned value encoded in LEB128, relative to their location ion memory. */
#define DW_EH_PE_absptr 0x00 /*!< The value itself is a pointer, it is not an offset. */
#define DW_EH_PE_pcrel 0x01 /*!< The value is an offset, relative to its location in memory. */
#define DW_EH_PE_datarel 0x03 /*!< The value is an offset, relative to .eh_frame_hdr section. */
/* Macros simplifying testing relative offset data encoding. */
#define ESP_ENCODING_PC_REL(ENCODING) (((ENCODING >> 4) & 0xf) == DW_EH_PE_pcrel)
#define ESP_ENCODING_FRAME_HDR_REL(ENCODING) (((ENCODING >> 4) & 0xf) == DW_EH_PE_datarel)
/**
* @brief Call Frame Information (CIE) fields information.
* As the size of CIE is variable, the simplest way to described it is to
* have a pointer at the beginning of CIE structure and access the fields
* thanks to the index macros defined here.
*/
#define ESP_CIE_VARIABLE_FIELDS_IDX (9) /*!< Offset, in bytes, where variable length fields start. */
/**
* @brief Frame Description Entry (FDE) fields index.
* For the same reasons as above, we prefer defining these macros rather than
* having a structure.
*/
#define ESP_FDE_LENGTH_IDX (0) /*!< Length, in bytes, of the FDE excluding this field. 4 bytes field. */
#define ESP_FDE_CIE_IDX (1) /*!< Nearest preceding Common Information Entry (CIE) offset. 4 bytes field. */
#define ESP_FDE_INITLOC_IDX (2) /*!< Initial location (of the function) the FDE describes. Variable size (encoding in CIE). */
#define ESP_FDE_RANGELEN_IDX (3) /*!< Size, in bytes, of the function described by this FDE location the FDE describes. Variable size (encoding in CIE). */
#define ESP_FDE_AUGMENTATION_IDX (4) /*!< Augmentation data length. Unsigned LEB128. */
/**
* @brief Pointers to both .eh_frame_hdr and .eh_frame sections.
*/
#define EH_FRAME_HDR_ADDR (&_eh_frame_hdr)
#define EH_FRAME_ADDR (&_eh_frame)
/**
* @brief Structure of .eh_frame_hdr section header.
*/
typedef struct {
uint8_t version; /*!< Structure version, must be 1.*/
uint8_t eh_frame_ptr_enc; /*!< eh_frame_ptr entry encoding. */
uint8_t fde_count_enc; /*!< fde_count entry encoding. */
uint8_t table_enc; /*!< table entries encoding. */
/* The rest of the structure has variable length. Thus, we cannot define
* them here. Here are their names:
* - eh_frame_ptr : encoded pointer to the .eh_frame section.
* - fde_Count : number of entries in the array of table_entry.
* - table_entry array : sorted array of table_entry. */
} __attribute__((packed)) fde_header;
/**
* @brief .eh_frame_hdr table's entry format.
* Each entry of the table contains 2 32-bit encoded addresses.
* Encoding is defined in the previous structure. Check table_enc field.
*/
typedef struct {
uint32_t fun_addr; /*!< Address of the function described. */
uint32_t fde_addr; /*!< Address of the FDE for the function.*/
} table_entry;
/**
* @brief DWARF state constant macros.
*/
#define ESP_EH_FRAME_STACK_SIZE (2) /*!< DWARF virtual machine can save the push the current on a virtual
stack. we mimic the stack with an array. While testing, a stack
size of 2 was enough. */
/**
* @brief
* Structure representing the state of the DWARF virtual machine.
*/
typedef struct {
/* Stack for DWARF state registers.
* For caller saved registers, save their CFA address (value in previous call frame).
* As these registers will be used to define offset in the CFA, they will always be
* multiple of CPU word (4-bytes in our case). Thus, it will save the offset in word-size, not
* in bytes. Plus, the highest bit will be used to mark that this register is NOY
* ESP_EH_FRAME_REG_SAME. (0x80000000 is a valid value then, meaning that the register value
* is CFA + 0 offset) */
uint32_t regs_offset[ESP_EH_FRAME_STACK_SIZE][EXECUTION_FRAME_MAX_REGS];
/* reg_offset represents the state of registers when PC reaches the following location. */
uint32_t location;
/* Index of the registers offset to use (1 for saved offset, 0 else). */
uint8_t offset_idx;
} dwarf_regs;
/**
* @brief DWARF's register state.
* When a DWARF register is set to ESP_EH_FRAME_REG_SAME, the CPU register corresponding to this
* virtual register will be unchanged after executing DWARF instructions.
* Please see esp_eh_frame_restore_caller_state() for more details.
*/
#define ESP_EH_FRAME_REG_SAME (0)
/**
* @brief Set a register's offset (relative to CFA).
* The highest bit is set to 1 to mark that this register needs to be retrived because it has been
* altered.
*/
#define ESP_EH_FRAME_SET_REG_OFFSET(offset) (0x80000000 | offset)
/**
* @brief Get a register's offset (relative to CFA).
*/
#define ESP_EH_FRAME_GET_REG_OFFSET(offset) (0x7fffffff & offset)
/**
* @brief Get a register's CFA offset.
*/
#define ESP_EH_FRAME_IS_CFA_RELATIVE(reg) ((reg >> 31) == 1)
/**
* @brief Test whether an offset is small enough to be stored
* in our 32-bit register.
* Note: the highest bit is used.
*/
#define ESP_EH_FRAME_CFA_OFFSET_VALID(offset) (offset < 0x80000000)
/**
* @brief Index of Call Frame Address (CFA) in DWARF registers array.
*/
#define ESP_ESH_FRAME_CFA_IDX (EXECUTION_FRAME_SP_REG)
/**
* @brief Macros to get and set CFA's relative register and offset.
* Indeed, CFA is defined by two values: register and offset. CFA is then
* calculated by adding the offset to the register value.
* `register` will be stored in the lowest 8 bits.
* `offset` will be stored in the highest 24 bits.
*
* NOTE: with this implementation, CFA will be affected by
* DW_CFA_REMEMBER_STATE and DW_CFA_RESTORE_STATE instructions.
*/
#if EXECUTION_FRAME_MAX_REGS > 255
#error "Too many registers defined for the target ExecutionFrame"
#endif
#define ESP_EH_FRAME_CFA_REG_VALID(reg) (reg < EXECUTION_FRAME_MAX_REGS)
#define ESP_EH_FRAME_CFA_OFF_VALID(off) (((off) >> 24) == 0)
#define ESP_EH_FRAME_CFA(state) ((state)->regs_offset[(state)->offset_idx][ESP_ESH_FRAME_CFA_IDX])
#define ESP_EH_FRAME_NEW_CFA(reg, off) (((off) << 8) | ((reg) & 0xff))
#define ESP_EH_FRAME_SET_CFA_REG(value, reg) (((value) & ~0xff) | ((reg) & 0xff))
#define ESP_EH_FRAME_SET_CFA_OFF(value, off) (((value) & 0xff) | ((off) << 8))
#define ESP_EH_FRAME_GET_CFA_REG(value) ((value) & 0xff)
#define ESP_EH_FRAME_GET_CFA_OFF(value) ((value) >> 8)
/**
* @brief Unsupported opcode value to return when exeucting 0-opcode type instructions.
*/
#define ESP_EH_FRAME_UNSUPPORTED_OPCODE ((uint32_t) -1)
/**
* @brief Macros defining the DWARF instructions code.
*/
#define DW_GET_OPCODE(OP) ((OP) >> 6)
#define DW_GET_PARAM(OP) ((OP) & 0b111111)
#define DW_CFA_ADVANCE_LOC (1)
#define DW_CFA_OFFSET (2)
#define DW_CFA_RESTORE (3)
/**
* @brief Constant for DWARF instructions code when high 2 bits are 0.
*/
#define DW_CFA_0_OPCODE (0)
#define DW_CFA_NOP (0x0)
#define DW_CFA_SET_LOC (0x1)
#define DW_CFA_ADVANCE_LOC1 (0x2)
#define DW_CFA_ADVANCE_LOC2 (0x3)
#define DW_CFA_ADVANCE_LOC4 (0x4)
#define DW_CFA_OFFSET_EXTENDED (0x5)
#define DW_CFA_RESTORE_EXTENDED (0x6)
#define DW_CFA_UNDEFINED (0x7)
#define DW_CFA_SAME_VALUE (0x8)
#define DW_CFA_REGISTER (0x9)
#define DW_CFA_REMEMBER_STATE (0xA)
#define DW_CFA_RESTORE_STATE (0xB)
#define DW_CFA_DEF_CFA (0xC)
#define DW_CFA_DEF_CFA_REGISTER (0xD)
#define DW_CFA_DEF_CFA_OFFSET (0xE)
#define DW_CFA_DEF_CFA_EXPRESSION (0xF)
#define DW_CFA_EXPRESSION (0x10)
#define DW_CFA_OFFSET_EXTENDED_SF (0x11)
#define DW_CFA_DEF_CFA_SF (0x12)
#define DW_CFA_DEF_CFA_OFFSET_SF (0x13)
#define DW_CFA_VAL_OFFSET (0x14)
#define DW_CFA_VAL_OFFSET_SF (0x15)
#define DW_CFA_VAL_EXPRESSION (0x16)
#define DW_CFA_LO_USER (0x1C)
/**
* @brief Constants used for decoding (U)LEB128 integers.
*/
#define DW_LEB128_HIGHEST_BIT(byte) (((byte) >> 7) & 1)
#define DW_LEB128_SIGN_BIT(byte) (((byte) >> 6) & 1)
#define DW_LEB128_MAX_SHIFT (31)
/**
* @brief Symbols defined by the linker.
* Retrieve the addresses of both .eh_frame_hdr and .eh_frame sections.
*/
extern char _eh_frame_hdr;
extern char _eh_frame;
/**
* @brief Decode multiple bytes encoded in LEB128.
*
* @param bytes bytes encoded in LEB128. They will not be modified.
* @param is_signed true if bytes represent a signed value, false else.
* @param size Size in bytes of the encoded value.
*
* @return Decoded bytes.
*/
static uint32_t decode_leb128(const uint8_t* bytes, bool is_signed, uint32_t* lebsize)
{
uint32_t res = 0;
uint32_t shf = 0;
uint32_t size = 0;
uint8_t byte = 0;
while(1) {
byte = bytes[size++];
res |= (byte & 0x7f) << shf;
shf += 7;
if (DW_LEB128_HIGHEST_BIT(byte) == 0)
break;
}
if (is_signed && shf <= DW_LEB128_MAX_SHIFT && DW_LEB128_SIGN_BIT(byte)) {
res |= ((uint32_t) ~0 << shf);
}
if (lebsize) {
*lebsize = size;
}
return res;
}
/**
* @brief Get the value of data encoded.
*
* @param data Pointer to the encoded data.
* @param encoding Encoding for the data to read.
* @param psize Reference to be filled with data size, in bytes.
*
* @return Decoded data read from the pointer.
*/
static uint32_t esp_eh_frame_get_encoded(void* data, uint8_t encoding, uint32_t* psize)
{
int32_t svalue = 0;
uint32_t uvalue = 0;
uint32_t fvalue = 0;
uint32_t size = 0;
const uint32_t high = encoding >> 4;
const uint32_t low = encoding & 0xf;
assert(psize != NULL);
if (encoding == DW_EH_PE_omit) {
*psize = size;
return uvalue;
}
switch (low) {
case DW_EH_PE_udata2:
size = 2;
uvalue = *((uint16_t*) data);
break;
case DW_EH_PE_udata4:
size = 4;
uvalue = *((uint32_t*) data);
break;
case DW_EH_PE_sdata2:
size = 2;
svalue = *((int16_t*) data);
break;
case DW_EH_PE_sdata4:
size = 4;
svalue = *((int32_t*) data);
break;
default:
/* Unsupported yet. */
assert(false);
break;
}
switch (high) {
case DW_EH_PE_absptr:
/* Do not change the values, as one of them will be 0, fvalue will
* contain the data no matter whether it is signed or unsigned. */
fvalue = svalue + uvalue;
break;
case DW_EH_PE_pcrel:
/* Relative to the address of the data.
* svalue has been casted to an 32-bit value, so even if it was a
* 2-byte signed value, fvalue will be calculated correctly here. */
fvalue = (uint32_t) data + svalue + uvalue;
break;
case DW_EH_PE_datarel:
fvalue = (uint32_t) EH_FRAME_HDR_ADDR + svalue + uvalue;
break;
}
*psize = size;
return fvalue;
}
/**
* @brief Find entry in the table for the given return_address.
*
* @param sorted_table Pointer to the sorted table of entries.
* @param length Number of entries in the table.
* @param encoding Encoding for the addresses in the table
* (Check DWARF documentation for more info about encoding).
* @param return_address The address to find in the table. This address can be
* part of one in the function listed.
*
* @note The table is structured like this (after decoding the addresses):
* Function address FDE address Index
* +-------------------------------+
* |0x403805a4 0x4038d014| 0
* +-------------------------------+
* |0x403805be 0x4038d034| 1
* +-------------------------------+
* |0x403805d8 0x4038d070| 2
* +-------------------------------+
* |.......... ..........| ...
* +-------------------------------+
* |0x42020c48 0x4038ddb4| length-3
* +-------------------------------+
* |0x42020dca 0x4038dde4| length-2
*+-------------------------------+
* |0x42020f92 0x4038debc| length-1
* +-------------------------------+
*
* For example, if return_address passed is 0x403805b4, this function will
* return a pointer to the entry (0x403805a4, 0x4038d014).
*
* @return Pointer to the entry found, NULL if not found.
*/
static const table_entry* esp_eh_frame_find_entry(const table_entry* sorted_table,
const uint32_t length,
const uint32_t encoding,
const uint32_t return_address)
{
int32_t ra = 0;
/* Used for decoding addresses in the table. */
uint32_t is_signed = (encoding & 0xf) >= 0x9;
uint32_t pc_relative = true;
/* The following local variables are used for dichotomic search. */
uint32_t found = false;
uint32_t begin = 0;
uint32_t end = length;
uint32_t middle = (end + begin) / 2;
/* If the addresses in the table are offsets relative to the eh_frame section,
* instead of decoding each of them, we can simply encode the return_address
* we have to find. If addresses are offsets relative to the programe counter,
* then we have no other choice than decoding each of them to compare them
* with return_address. */
if (ESP_ENCODING_FRAME_HDR_REL(encoding)) {
ra = return_address - (uint32_t) EH_FRAME_HDR_ADDR;
pc_relative = false;
}
/* Perform dichotomic search. */
while (end != 0 && middle != (length - 1) && !found) {
const uint32_t fun_addr = sorted_table[middle].fun_addr;
const uint32_t nxt_addr = sorted_table[middle + 1].fun_addr;
if (pc_relative) {
ra = return_address - (uint32_t) (sorted_table + middle);
}
if (is_signed) {
/* Signed comparisons. */
const int32_t sfun_addr = (int32_t) fun_addr;
const int32_t snxt_addr = (int32_t) nxt_addr;
if (sfun_addr <= ra && snxt_addr > ra)
found = true;
else if (snxt_addr <= ra)
begin = middle + 1;
else
end = middle;
} else {
/* Unsigned comparisons. */
const uint32_t ura = (uint32_t) ra;
if (fun_addr <= ura && nxt_addr > ura)
found = true;
else if (nxt_addr <= ura)
begin = middle + 1;
else
end = middle;
}
middle = (end + begin) / 2;
}
/* If 'end' reached the beginning of the array, it means the return_address
* passed was below the first address of the array, thus, it was wrong.
* Else, return the address found. */
return (end == 0) ? 0 : sorted_table + middle;
}
/**
* @brief Decode an address according to the encoding passed.
*
* @param addr Pointer to the address to decode.
* This pointer's value MUST be an address in .eh_frame_hdr section.
* @param encoding DWARF encoding byte.
*
* @return address dedoded (e.g. absolute address)
*/
static inline uint32_t* esp_eh_frame_decode_address(const uint32_t* addr,
const uint32_t encoding)
{
uint32_t* decoded = 0;
if (ESP_ENCODING_FRAME_HDR_REL(encoding))
decoded = (uint32_t*) (*addr + (uint32_t) EH_FRAME_HDR_ADDR);
else if (ESP_ENCODING_PC_REL(encoding))
decoded = (uint32_t*) (*addr + (uint32_t) addr);
else
decoded = (uint32_t*) (*addr);
return decoded;
}
/**
* @brief Execute the DWARF instruction which high 2 bits are 0.
*
* @param opcode low 6 bits of the instruction code.
* @param operands pointer to the possible operands.
* @param state state of the DWARF machine. Its registers may be modified.
*
* @return Number of operands used for executing the instruction.
*/
static inline uint32_t esp_eh_frame_execute_opcode_0(const uint32_t opcode, const uint8_t* operands,
dwarf_regs* state)
{
uint32_t operand1 = 0;
uint32_t used_operands = 0;
uint32_t operand2 = 0;
uint32_t used_operands2 = 0;
switch(opcode) {
case DW_CFA_NOP:
break;
case DW_CFA_ADVANCE_LOC1:
/* Advance location with a 1-byte delta. */
used_operands = 1;
state->location += *operands;
break;
case DW_CFA_ADVANCE_LOC2:
/* Advance location with a 2-byte delta. */
used_operands = 2;
state->location += *((const uint16_t*) operands);
break;
case DW_CFA_ADVANCE_LOC4:
/* Advance location with a 4-byte delta. */
used_operands = 4;
state->location += *((const uint32_t*) operands);
break;
case DW_CFA_REMEMBER_STATE:
assert(state->offset_idx == 0);
memcpy(state->regs_offset[1], state->regs_offset[0],
EXECUTION_FRAME_MAX_REGS * sizeof(uint32_t));
state->offset_idx++;
break;
case DW_CFA_RESTORE_STATE:
assert(state->offset_idx == 1);
/* Drop the saved state. */
state->offset_idx--;
break;
case DW_CFA_DEF_CFA:
/* CFA changes according to a register and an offset.
* This instruction appears when the assembly code saves the
* SP in the middle of a routine, before modifying it.
* For example (on RISC-V):
* addi s0, sp, 80
* addi sp, sp, -10
* ... */
/* Operand1 is the register containing the CFA value. */
operand1 = decode_leb128(operands, false, &used_operands);
/* Offset for the register's value. */
operand2 = decode_leb128(operands + used_operands, false, &used_operands2);
/* Calculate the number of bytes */
used_operands += used_operands2;
/* Assert that the register and the offset are valid. */
assert(ESP_EH_FRAME_CFA_REG_VALID(operand1));
assert(ESP_EH_FRAME_CFA_OFF_VALID(operand2));
ESP_EH_FRAME_CFA(state) = ESP_EH_FRAME_NEW_CFA(operand1, operand2);
break;
case DW_CFA_DEF_CFA_REGISTER:
/* Define the register of the current frame address (CFA).
* Its operand is in the next bytes, its type is ULEB128. */
operand1 = decode_leb128(operands, false, &used_operands);
/* Check whether the value is valid or not. */
assert(ESP_EH_FRAME_CFA_OFF_VALID(operand1));
/* Offset will be unchanged, only register changes. */
ESP_EH_FRAME_CFA(state) = ESP_EH_FRAME_SET_CFA_REG(ESP_EH_FRAME_CFA(state), operand1);
break;
case DW_CFA_DEF_CFA_OFFSET:
/* Same as above but for the offset. The register of CFA remains unchanged. */
operand1 = decode_leb128(operands, false, &used_operands);
assert(ESP_EH_FRAME_CFA_OFF_VALID(operand1));
ESP_EH_FRAME_CFA(state) = ESP_EH_FRAME_SET_CFA_OFF(ESP_EH_FRAME_CFA(state), operand1);
break;
default:
panic_print_str("\r\nUnsupported DWARF opcode 0: 0x");
panic_print_hex(opcode);
panic_print_str("\r\n");
used_operands = ESP_EH_FRAME_UNSUPPORTED_OPCODE;
break;
}
return used_operands;
}
/**
* @brief Execute DWARF instructions.
*
* @param instructions Array of instructions to execute.
* @param instructions_length Length of the array of instructions.
* @param frame Execution frame of the crashed task. This will only be used to
* get the PC where the task crashed.
* @param state DWARF machine state. The registers contained in the state will
* modified accordingly to the instructions.
*
* @return true if the execution went fine, false if an unsupported instruction was met.
*/
static bool esp_eh_frame_execute(const uint8_t* instructions, const uint32_t instructions_length,
const ExecutionFrame* frame, dwarf_regs* state)
{
for (uint32_t i = 0; i < instructions_length; i++) {
const uint8_t instr = instructions[i];
const uint8_t param = DW_GET_PARAM(instr);
uint32_t operand1 = 0;
uint32_t size = 0;
uint32_t used_operands = 0;
/* Decode the instructions. According to DWARF documentation, there are three
* types of Call Frame Instructions. The upper 2 bits defines the type. */
switch (DW_GET_OPCODE(instr)) {
case DW_CFA_0_OPCODE:
used_operands = esp_eh_frame_execute_opcode_0(param, &instructions[i + 1], state);
/* Exit the function if an unsupported opcode was met. */
if (used_operands == ESP_EH_FRAME_UNSUPPORTED_OPCODE) {
return false;
}
i += used_operands;
break;
case DW_CFA_ADVANCE_LOC:
/* Move the location forward. This instruction will mark when to stop:
* once we reach the instruction where the PC left, we can break out of the loop
* The delta is part of the lowest 6 bits.
*/
state->location += param;
break;
case DW_CFA_OFFSET:
operand1 = decode_leb128(&instructions[i + 1], false, &size);
assert(ESP_EH_FRAME_CFA_OFFSET_VALID(operand1));
state->regs_offset[state->offset_idx][param] = ESP_EH_FRAME_SET_REG_OFFSET(operand1);
i += size;
break;
case DW_CFA_RESTORE:
state->regs_offset[state->offset_idx][param] = ESP_EH_FRAME_REG_SAME;
break;
default:
/* Illegal opcode */
assert(false);
break;
}
/* As the state->location can also be modified by 0-opcode instructions (in the function)
* and also because we need to break the loop (and not only the switch), let's put this
* check here, after the execution of the instruction, outside of the switch block. */
if (state->location >= EXECUTION_FRAME_PC(*frame))
break;
}
/* Everything went fine, no unsupported opcode was met, return true. */
return true;
}
/**
* @brief Initialize the DWARF registers state by parsing and executing CIE instructions.
*
* @param cie Pointer to the CIE data.
* @param frame Pointer to the execution frame.
* @param state DWARF machine state (DWARF registers).
*
* @return index of the DWARF register containing the return address.
*/
static uint32_t esp_eh_frame_initialize_state(const uint8_t* cie, ExecutionFrame* frame, dwarf_regs* state)
{
char c = 0;
uint32_t size = 0;
/* The first word in the CIE structure is the length of the structure,
* excluding this field itself. */
const uint32_t length = ((uint32_t*) cie)[0];
/* ID of the CIE, should be 0 for .eh_frame (which is our case) */
const uint32_t id = ((uint32_t*) cie)[1];
assert(id == 0);
/* Ignore CIE version (1 byte). */
/* The following data in the structure have variable length as they are
* encoded in (U)LEB128. Thus, let's use a byte pointer to parse them. */
uint8_t* cie_data = (uint8_t*) cie + ESP_CIE_VARIABLE_FIELDS_IDX;
/* Next field is a null-terminated UTF-8 string. Ignore it, look for the end. */
while((c = *cie_data++) != 0);
/* Field alignement factor shall be 1. It is encoded in ULEB128. */
const uint32_t code_align = decode_leb128(cie_data, false, &size);
assert(code_align == 1);
/* Jump to the next field */
cie_data += size;
/* Same goes for data alignement factor. Shall be equal to -4. */
const int32_t data_align = decode_leb128(cie_data, true, &size);
cie_data += size;
assert(data_align == -4);
/* Field describing the index of the DWARF register which will contain
* the return address. */
const uint32_t ra_reg = decode_leb128(cie_data, false, &size);
cie_data += size;
/* Augmentation data length is encoded in ULEB128. It represents the,
* length of the augmentation data. Jump after it to retrieve the
* instructions to execute. */
const uint32_t augmentation_len = decode_leb128(cie_data, false, &size);
cie_data += size + augmentation_len;
/* Calculate the instructions length in order to prevent any out of bounds
* bug. Subtract the offset of this field (minus sizeof(uint32_t) because
* `length` field is not part of the structure length) to the total length
* of the structure. */
const uint32_t instructions_length = length - (cie_data - sizeof(uint32_t) - cie);
/* Execute the instructions contained in CIE structure. Their goal is to
* initialize the DWARF registers. Usually it binds the CFA (virtual stack
* pointer), to its hardware equivalent. It will also bind a hardware
* register to the virtual return address register. For example, x86
* doesn't have a return address register, the address to return to
* it stored on the stack when `call` instruction is used. DWARF will
* use `eip` (instruction pointer, a.k.a. program counter) as a
* register containing the return address register. */
esp_eh_frame_execute(cie_data, instructions_length, frame, state);
return ra_reg;
}
/**
* @brief Modify the execution frame and DWARF VM state for restoring caller's context.
*
* @param fde Pointer to the Frame Description Entry for the current program counter (defined by frame's MEPC register)
* @param frame Snapshot of the CPU registers when the CPU stopped its normal execution.
* @param state DWARF VM registers.
*
* @return Return Address of the current context. Frame has been restored to the previous context
* (before calling the function program counter is currently going throught).
*/
static uint32_t esp_eh_frame_restore_caller_state(const uint32_t* fde,
ExecutionFrame* frame,
dwarf_regs* state)
{
/* Length of the whole Frame Description Entry (FDE), excluding this field. */
const uint32_t length = fde[ESP_FDE_LENGTH_IDX];
/* The addresses in FDE are relative to the location of each field.
* Thus, to get the absolute address of the function it is pointing to,
* we have to compute:
* fun_addr = &fde[IDX] +/- fde[IDX]
*/
const uint8_t* cie = (uint8_t*) ((uint32_t) &fde[ESP_FDE_CIE_IDX] - fde[ESP_FDE_CIE_IDX]);
const uint32_t initial_location = ((uint32_t) &fde[ESP_FDE_INITLOC_IDX] + fde[ESP_FDE_INITLOC_IDX]);
const uint32_t range_length = fde[ESP_FDE_RANGELEN_IDX];
const uint8_t augmentation = *((uint8_t*) (fde + ESP_FDE_AUGMENTATION_IDX));
/* The length, in byte, of the instructions is the size of the FDE header minus
* the above fields' length. */
const uint32_t instructions_length = length - 3 * sizeof(uint32_t) - sizeof(uint8_t);
const uint8_t* instructions = ((uint8_t*) (fde + ESP_FDE_AUGMENTATION_IDX)) + 1;
/* Make sure this FDE is the correct one for the PC given. */
assert(initial_location <= EXECUTION_FRAME_PC(*frame) &&
EXECUTION_FRAME_PC(*frame) < initial_location + range_length);
/* Augmentation not supported. */
assert(augmentation == 0);
/* Initialize the DWARF state by executing the CIE's instructions. */
const uint32_t ra_reg = esp_eh_frame_initialize_state(cie, frame, state);
state->location = initial_location;
/**
* Execute the DWARf instructions is order to create rules that will be executed later to retrieve
* the registers former value.
*/
bool success = esp_eh_frame_execute(instructions, instructions_length, frame, state);
if (!success) {
/* An error occured (unsupported opcode), return PC as the return address.
* This will be tested by the caller, and the backtrace will be finished. */
return EXECUTION_FRAME_PC(*frame);
}
/* Execute the rules calculated previously. Start with the CFA. */
const uint32_t cfa_val = ESP_EH_FRAME_CFA(state);
const uint32_t cfa_reg = ESP_EH_FRAME_GET_CFA_REG(cfa_val);
const uint32_t cfa_off = ESP_EH_FRAME_GET_CFA_OFF(cfa_val);
const uint32_t cfa_addr = EXECUTION_FRAME_REG(frame, cfa_reg) + cfa_off;
/* Restore the registers that need to be restored. */
for (uint32_t i = 0; i < DIM(state->regs_offset[0]); i++) {
uint32_t value_addr = state->regs_offset[state->offset_idx][i];
/* Check that the value changed and that we are not treating the CFA register (if it is part of the array). */
if (i != ESP_ESH_FRAME_CFA_IDX && value_addr != ESP_EH_FRAME_REG_SAME) {
/* value_addr contains a description of how to find its address:
* it has an offset relative to the CFA, which will point to the actual former value.
* In fact, the register's previous value (in the context of the caller) is on the stack,
* this is what value_addr will point to. */
value_addr = cfa_addr - ESP_EH_FRAME_GET_REG_OFFSET(value_addr) * sizeof(uint32_t);
EXECUTION_FRAME_REG(frame, i) = *((uint32_t*) value_addr);
}
}
/* Restore the stack pointer according to DWARF CFA register. */
EXECUTION_FRAME_SP(*frame) = cfa_addr;
/* If the frame was not available, it would be possible to retrieve the return address
* register thanks to CIE structure. */
return EXECUTION_FRAME_REG(frame, ra_reg);
}
/**
* @brief When one step of the backtrace is generated, output it to the serial.
* This function can be overriden as it is defined as weak.
*
* @param pc Program counter of the backtrace step.
* @param sp Stack pointer of the backtrace step.
*/
void __attribute__((weak)) esp_eh_frame_generated_step(uint32_t pc, uint32_t sp)
{
panic_print_str(" 0x");
panic_print_hex(pc);
panic_print_str(":0x");
panic_print_hex(sp);
}
/**
* @brief Print backtrace for the given execution frame.
*
* @param frame_or Snapshot of the CPU registers when the CPU stopped its normal execution.
*/
void esp_eh_frame_print_backtrace(const ExecutionFrame *frame_or)
{
static dwarf_regs state = { 0 };
ExecutionFrame frame = *frame_or;
uint32_t size = 0;
uint8_t* enc_values = NULL;
bool end_of_backtrace = false;
/* Start parsing the .eh_frame_hdr section. */
fde_header* header = (fde_header*) EH_FRAME_HDR_ADDR;
assert(header->version == 1);
/* 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;
assert(((table_enc >> 4) == 0x3) || ((table_enc >> 4) == 0xB));
const table_entry* sorted_table = (const table_entry*) enc_values;
panic_print_str("Backtrace:");
while (!end_of_backtrace) {
/* Output one step of the backtrace. */
esp_eh_frame_generated_step(EXECUTION_FRAME_PC(frame), EXECUTION_FRAME_SP(frame));
const table_entry* from_fun = esp_eh_frame_find_entry(sorted_table, fde_count,
table_enc, EXECUTION_FRAME_PC(frame));
if (from_fun == 0) {
/* Address was not found in the list. */
break;
}
/* Get absolute address of FDE entry describing the function where PC left of. */
const uint32_t* fde = esp_eh_frame_decode_address(&from_fun->fde_addr, table_enc);
/* Clean and set the DWARF register structure.
* TODO: Initialization should be done by the instruction contained by the CIE associated to the FDE. */
memset(&state, 0, sizeof(dwarf_regs));
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. */
end_of_backtrace = (EXECUTION_FRAME_SP(frame) == prev_sp) && (EXECUTION_FRAME_PC(frame) == ra);
/* Go back to the caller: update stack pointer and program counter. */
EXECUTION_FRAME_PC(frame) = ra;
}
panic_print_str("\r\n");
}
#endif //ESP_SYSTEM_USE_EH_FRAME

Wyświetl plik

@ -28,6 +28,11 @@
#endif
#endif
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
#include "port/eh_frame_parser.h"
#endif
#define DIM(array) (sizeof(array)/sizeof(*array))
/**
@ -321,6 +326,9 @@ void panic_arch_fill_info(void *frame, panic_info_t *info)
void panic_print_backtrace(const void *frame, int core)
{
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
esp_eh_frame_print_backtrace(frame);
#else
// Basic backtrace
panic_print_str("\r\nStack memory:\r\n");
uint32_t sp = (uint32_t)((RvExcFrame *)frame)->sp;
@ -335,6 +343,7 @@ void panic_print_backtrace(const void *frame, int core)
panic_print_str(y == per_line - 1 ? "\r\n" : " ");
}
}
#endif
}
uint32_t panic_get_address(const void *f)

Wyświetl plik

@ -0,0 +1,37 @@
#
# 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.
CC=gcc
CFLAGS=-W -fasynchronous-unwind-tables -I. -std=c99 -g -DCONFIG_ESP_SYSTEM_USE_EH_FRAME -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
BIN=eh_frame_test
.PHONY: all clean createdir
all: $(OBJECTS)
$(CC) -o $(BIN) $^ $(LDFLAGS)
objs/main.o:main.c
@mkdir -p objs
$(CC) -o $@ -c $^ $(CFLAGS)
objs/eh_frame_parser.o: ../eh_frame_parser.c
@mkdir -p objs
$(CC) -o $@ -c $^ $(CFLAGS)
clean:
rm -rf objs $(BIN)

Wyświetl plik

@ -0,0 +1,32 @@
// 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.
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>
static inline void panic_print_str(const char* str)
{
/* Ignore "Backtrace:" string. */
if (strcmp(str, "Backtrace:") != 0)
printf("%s", str);
}
static inline void panic_print_hex(const uint32_t value)
{
printf("%x", value);
}

Wyświetl plik

@ -0,0 +1,31 @@
/**
* 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.
*/
SECTIONS
{
.eh_frame_hdr :
{
_eh_frame_hdr = ABSOLUTE(.);
*(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*)
}
.eh_frame : ONLY_IF_RO
{
_eh_frame = ABSOLUTE(.);
KEEP (*(.eh_frame)) *(.eh_frame.*)
}
}
INSERT BEFORE .gcc_except_table;

Wyświetl plik

@ -0,0 +1,307 @@
// 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`.
*/
#define _POSIX_C_SOURCE 199309L
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <ucontext.h>
#include "port/eh_frame_parser.h"
/**
* @brief Index of x86 registers in `greg_t` structure.
*/
#define REG_EDI 4
#define REG_ESI 5
#define REG_EBP 6
#define REG_ESP 7
#define REG_EBX 8
#define REG_EDX 9
#define REG_ECX 10
#define REG_EAX 11
#define REG_EIP 14
/**
* @brief Number of functions in the funs structure described below.
*/
#define FUNCTIONS_COUNT ((sizeof(funs)/sizeof(*funs)))
/**
* @brief Number which will determine the depth of the call stack.
* Check `main()` for more information.
*/
#define NUMBER_TO_TEST (4)
/**
* @brief Define a simple linked list type and initialize one.
*/
struct list_t {
uint32_t value;
struct list_t *next;
};
static struct list_t head = { 0 };
/**
* Few recursive functions to make the the call stack a bit more complex than a
* single function call would give.
*/
bool is_odd(uint32_t n);
bool is_even(uint32_t n);
void browse_list(struct list_t* l);
/**
* @brief Structure defining a function of our program.
* This will be used to translate the backtrace.
*/
struct functions_info {
const char* name;
uintptr_t start;
uintptr_t end; /* will be filled at runtime */
};
/**
* @brief Structure storing the information about the
* function that will be part of the backtrace.
*/
struct functions_info funs[] = {
{
.name = "browse_list",
.start = (uintptr_t) &browse_list,
.end = 0
},
{
.name = "is_odd",
.start = (uintptr_t) &is_odd,
.end = 0
},
{
.name = "is_even",
.start = (uintptr_t) &is_even,
.end = 0
}
};
/**
* @brief Test whether the address passed as PC is part of the function which
* name is `funciton_name`. The global array `funs` is used.
*
* @param pc Program counter to test. (address in the program)
* @param function_name Function name to check the address of.
*
* @return true if PC is in the function called `function_name`, false else.
*/
bool is_pc_in_function(const uint32_t pc, const char* function_name)
{
for (uint32_t i = 0; i < FUNCTIONS_COUNT; i++) {
const struct functions_info current_fun = funs[i];
if (strcmp(current_fun.name, function_name) == 0) {
return current_fun.start <= pc && pc <= current_fun.end;
}
}
/* Function not found. */
return false;
}
/**
* @brief Override the default function called when a backtrace step is
* generated.
*/
void esp_eh_frame_generated_step(uint32_t pc, uint32_t sp) {
/* Number of times this function has been entered. */
static uint32_t iteration = 1;
/* The first PC in the backtrace are calls to `browse_list()`.
* +2 is due to the fact that the list contains all the numbers
* between NUMBER_TO_TEST to 0 included. Moreover, another call
* is made when we meet the NULL pointer.
*/
if (iteration > 0 && iteration <= (NUMBER_TO_TEST + 2)) {
assert(is_pc_in_function(pc, "browse_list"));
} else {
/**
* The backtrace should be:
* - in is_odd when iteration is even.
* - in is_even when iteration is odd.
*
* The backtrace finishes when the iteration reaches the end of
* browse_list (NUMBER_TO_TEST + 2 iterations), is_even
* (NUMBER_TO_TEST/2 calls) and is_odd (NUMBER_TO_TEST/2 calls) calls.
*/
if (iteration > (2 * NUMBER_TO_TEST + 2))
return;
else if (iteration % 2 == 0)
assert(is_pc_in_function(pc, "is_odd"));
else
assert(is_pc_in_function(pc, "is_even"));
}
iteration++;
}
/**
* @brief Handler called when SIGSEV signal is sent to the program.
*
* @param signal Signal received byt 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.
*/
void signal_handler(int signal, siginfo_t *info, void *ucontext) {
/* Setup the execution frame as expected by the eh_frame_parser.
* Indeed, the registers index defined in ucontext.h are NOT the same
* the registers index DWARF is expecting. */
ucontext_t* context = (ucontext_t*) ucontext;
greg_t *gregset = context->uc_mcontext.__gregs;
x86ExcFrame frame = {
.eax = gregset[REG_EAX],
.ecx = gregset[REG_ECX],
.edx = gregset[REG_EDX],
.ebx = gregset[REG_EBX],
.esp = gregset[REG_ESP],
.ebp = gregset[REG_EBP],
.esi = gregset[REG_ESI],
.edi = gregset[REG_EDI],
.eip = gregset[REG_EIP]
};
/* The following function will use panic_print_str and panic_print_hex
* function to output the data.
* Instead of replacing stdout file descriptor with a pipe, we can simply
* replace these functions to store the data instead of printing them.
*/
esp_eh_frame_print_backtrace(&frame);
/* No assert has been triggered, the backtrace succeeded. */
printf("\e[32m\e[1mAll tests passed \e[0m\r\n");
/* Everything went fine, exit normally. */
exit(0);
}
/**
* @brief Browse the list passed as an argument.
* The following function will trigger a SIGSEV signal on purpose, in order to
* generate the backtrace.
*
* @param l List to browse.
*/
void browse_list(struct list_t* l) {
browse_list(l->next);
}
/**
* @brief Add a number to the global list `head`.
*
* @param n Number to add in the list.
*/
void add_number_to_list(uint32_t n) {
struct list_t* l = malloc(sizeof(struct list_t));
l->value = n;
l->next = head.next;
head.next = l;
}
/**
* @brief Test if the number passed is even.
* This function will fail, on purpose.
*
* @param n Number to test.
*
* @return true if even, false else.
*/
bool is_even(uint32_t n) {
add_number_to_list(n);
if (n == 0) {
browse_list(head.next);
return true;
}
return is_odd(n - 1);
}
/**
* @brief Test if the number passed is odd.
* This function will fail, on purpose.
*
* @param n Number to test.
*
* @return true if odd, false else.
*/
bool is_odd(uint32_t n) {
add_number_to_list(n);
if (n == 0) {
browse_list(head.next);
return false;
}
return is_even(n - 1);
}
/**
* @brief Initiliaze the global `funs` array.
*/
static inline void initialize_functions_info(void)
{
for (uint32_t i = 0; i < FUNCTIONS_COUNT; i++) {
/* Each of the functions defined in this structure finishes
* with the following instructions:
* leave (0xc9)
* ret (0xc3)
* Thus, we will look for these instructions. */
uint8_t* instructions = (uint8_t*) funs[i].start;
while (instructions[0] != 0xc9 || instructions[1] != 0xc3)
instructions++;
instructions += 1;
funs[i].end = (uintptr_t) instructions;
}
}
/**
* Call the previous functions to create a complex call stack and fail.
*/
int main (int argc, char** argv)
{
/* Initialize the structure holding information about the signal to override. */
struct sigaction sig = {
.sa_mask = 0,
.sa_flags = SA_SIGINFO,
.sa_restorer = NULL,
.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");
}
/* Trigger the segmentation fault with a complex backtrace. */
is_even(NUMBER_TO_TEST);
return 0;
}

Wyświetl plik

@ -0,0 +1,27 @@
// 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.
#ifndef EH_FRAME_PARSER_H
#define EH_FRAME_PARSER_H
#include "eh_frame_parser_impl.h"
/**
* @brief Print backtrace for the given execution frame.
*
* @param frame_or Snapshot of the CPU registers when the CPU stopped its normal execution.
*/
void esp_eh_frame_print_backtrace(const ExecutionFrame *frame_or);
#endif

Wyświetl plik

@ -0,0 +1,83 @@
// 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

Wyświetl plik

@ -0,0 +1,27 @@
// 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.
#ifndef EH_FRAME_PARSER_H
#define EH_FRAME_PARSER_H
#include "eh_frame_parser_impl.h"
/**
* @brief Print backtrace for the given execution frame.
*
* @param frame_or Snapshot of the CPU registers when the CPU stopped its normal execution.
*/
void esp_eh_frame_print_backtrace(const ExecutionFrame *frame_or);
#endif

Wyświetl plik

@ -0,0 +1,65 @@
// 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 RISC-V, required for
* parsing `eh_frame` and `eh_frame_hdr`.
*
*/
#ifndef EH_FRAME_PARSER_IMPL_H
#define EH_FRAME_PARSER_IMPL_H
#include "riscv/rvruntime-frames.h"
/**
* @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)])
#endif // _EH_FRAME_PARSER_IMPL_H

Wyświetl plik

@ -6,6 +6,8 @@ set(srcs "clk.c"
"../../arch/riscv/expression_with_stack.c"
"../../arch/riscv/expression_with_stack_asm.S"
"../../arch/riscv/panic_arch.c")
set(srcs ${srcs} "../../arch/riscv/eh_frame_parser.c")
add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" ${srcs})
target_sources(${COMPONENT_LIB} PRIVATE ${srcs})

Wyświetl plik

@ -296,6 +296,10 @@ esp_err_t esp_flash_deinit_os_functions(esp_flash_t* chip)
esp_err_t esp_flash_init_main_bus_lock(void)
{
/* The following called functions are only defined if CONFIG_SPI_FLASH_SHARE_SPI1_BUS
* is set. Thus, we must not call them if the macro is not defined, else the linker
* would trigger errors. */
#if CONFIG_SPI_FLASH_SHARE_SPI1_BUS
spi_bus_lock_init_main_bus();
spi_bus_lock_set_bg_control(g_main_spi_bus_lock, cache_enable, cache_disable, NULL);
@ -304,6 +308,9 @@ esp_err_t esp_flash_init_main_bus_lock(void)
return err;
}
return ESP_OK;
#else
return ESP_ERR_NOT_SUPPORTED;
#endif
}
esp_err_t esp_flash_app_enable_os_functions(esp_flash_t* chip)