From 890636dfb2fabd6ee347962e359a3c258d8933f1 Mon Sep 17 00:00:00 2001 From: Baseline-K Date: Mon, 10 Aug 2026 15:52:47 +0800 Subject: [PATCH] support call stack backtrace through user code regions Cortex-M7 etc. can execute time-critical functions from RAM (ITCM) or split the firmware across multiple code regions. The default scan only recognizes one Flash code region (CMB_CODE_SECTION_NAME), so frames that cross the Flash boundary are dropped and the call stack breaks at the most critical code. Add an optional user code section backtrace: - cmb_cfg.h: CMB_USING_USER_CODE_SECTION switch; when ON, CMB_USER_CODE_SECTIONS declares extra code regions as a const {start,end} array initializer. Flash is always auto-detected via CMB_CODE_SECTION_NAME. - cmb_def.h: CMB_CODE_REGION_MAX (default 8), the code region table capacity. - cm_backtrace.c: cm_backtrace_init() fills code_regions[] (Flash first, then user-defined); cmb_pc_in_code()/cmb_pc_in_code_skip_first() walk the table. - When CMB_USING_USER_CODE_SECTION is not defined all new code is stripped by the preprocessor, byte-identical to v1.5.0. Co-Authored-By: Claude Fable 5 --- cm_backtrace/cm_backtrace.c | 77 +++++++++++++++++++++++++++++++++++++ cm_backtrace/cmb_cfg.h | 7 ++++ cm_backtrace/cmb_def.h | 5 +++ 3 files changed, 89 insertions(+) diff --git a/cm_backtrace/cm_backtrace.c b/cm_backtrace/cm_backtrace.c index 8ed273b..f532842 100644 --- a/cm_backtrace/cm_backtrace.c +++ b/cm_backtrace/cm_backtrace.c @@ -139,6 +139,24 @@ static bool statck_has_fpu_regs = false; static bool on_thread_before_fault = false; +#ifdef CMB_USING_USER_CODE_SECTION +/* multiple code segments support: Flash code + user-defined code regions. + cm_backtrace_init() fills this table from the Flash region (CMB_CODE_SECTION_NAME) + plus the user-defined code sections (CMB_USER_CODE_SECTIONS). */ +typedef struct { + uint32_t start; /* code region base address (inclusive) */ + uint32_t end; /* code region end address (inclusive) */ +} cmb_code_region_t; + +static cmb_code_region_t code_regions[CMB_CODE_REGION_MAX] = { { 0, 0 } }; +static uint8_t code_region_cnt = 0; + +#ifdef CMB_USER_CODE_SECTIONS +/* user-defined additional code regions, configured in cmb_cfg.h (const array) */ +static const cmb_code_region_t user_code_sections[] = CMB_USER_CODE_SECTIONS; +#endif /* CMB_USER_CODE_SECTIONS */ +#endif /* CMB_USING_USER_CODE_SECTION */ + /** * library initialize */ @@ -166,6 +184,28 @@ void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, cons #error "not supported compiler" #endif +#ifdef CMB_USING_USER_CODE_SECTION + /* fill code region table: Flash code region first, then user-defined regions */ + code_region_cnt = 0; + if (code_size != 0) { + code_regions[code_region_cnt].start = code_start_addr; + code_regions[code_region_cnt].end = code_start_addr + code_size; + code_region_cnt++; + } +#ifdef CMB_USER_CODE_SECTIONS + { + size_t i, n = sizeof(user_code_sections) / sizeof(user_code_sections[0]); + for (i = 0; i < n; i++) { + if ((code_region_cnt < CMB_CODE_REGION_MAX) && (user_code_sections[i].start < user_code_sections[i].end)) { + code_regions[code_region_cnt].start = user_code_sections[i].start; + code_regions[code_region_cnt].end = user_code_sections[i].end; + code_region_cnt++; + } + } + } +#endif /* CMB_USER_CODE_SECTIONS */ +#endif /* CMB_USING_USER_CODE_SECTION */ + if (main_stack_size == 0) { cmb_println(print_info[PRINT_MAIN_STACK_CFG_ERROR]); return; @@ -305,6 +345,31 @@ static bool disassembly_ins_is_bl_blx(uint32_t addr) { } } +#ifdef CMB_USING_USER_CODE_SECTION +/* check whether pc falls in any code region (Flash code or user-defined code) */ +static bool cmb_pc_in_code(uint32_t pc) { + uint8_t i; + for (i = 0; i < code_region_cnt; i++) { + if ((pc >= code_regions[i].start) && (pc <= code_regions[i].end)) { + return true; + } + } + return false; +} + +/* check whether pc falls in any code region, skipping the first word of each region + to reduce false positives (the existing Flash-only check used the same bias) */ +static bool cmb_pc_in_code_skip_first(uint32_t pc) { + uint8_t i; + for (i = 0; i < code_region_cnt; i++) { + if ((pc >= code_regions[i].start + sizeof(size_t)) && (pc <= code_regions[i].end)) { + return true; + } + } + return false; +} +#endif /* CMB_USING_USER_CODE_SECTION */ + size_t cm_backtrace_call_stack_any(uint32_t *buffer, size_t size, uint32_t sp, uint32_t stack_start_addr, uint32_t stack_size) { uint32_t pc; @@ -319,7 +384,11 @@ size_t cm_backtrace_call_stack_any(uint32_t *buffer, size_t size, uint32_t sp, u } /* fix the PC address in thumb mode */ pc = *((uint32_t *) sp) - 1; +#ifdef CMB_USING_USER_CODE_SECTION + if (cmb_pc_in_code_skip_first(pc) && (depth < CMB_CALL_STACK_MAX_DEPTH) +#else if ((pc >= code_start_addr + sizeof(size_t)) && (pc <= code_start_addr + code_size) && (depth < CMB_CALL_STACK_MAX_DEPTH) +#endif /* check the the instruction before PC address is 'BL' or 'BLX' */ && disassembly_ins_is_bl_blx(pc - sizeof(size_t)) && (depth < size)) { /* the second depth function may be already saved, so need ignore repeat */ @@ -354,7 +423,11 @@ size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp) { buffer[depth++] = regs.saved.pc; /* fix the LR address in thumb mode */ pc = regs.saved.lr - 1; +#ifdef CMB_USING_USER_CODE_SECTION + if (cmb_pc_in_code(pc) && (depth < CMB_CALL_STACK_MAX_DEPTH) +#else if ((pc >= code_start_addr) && (pc <= code_start_addr + code_size) && (depth < CMB_CALL_STACK_MAX_DEPTH) +#endif && (depth < size)) { buffer[depth++] = pc; regs_saved_lr_is_valid = true; @@ -389,7 +462,11 @@ size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp) { } /* fix the PC address in thumb mode */ pc = *((uint32_t *) sp) - 1; +#ifdef CMB_USING_USER_CODE_SECTION + if (cmb_pc_in_code_skip_first(pc) && (depth < CMB_CALL_STACK_MAX_DEPTH) +#else if ((pc >= code_start_addr + sizeof(size_t)) && (pc <= code_start_addr + code_size) && (depth < CMB_CALL_STACK_MAX_DEPTH) +#endif /* check the the instruction before PC address is 'BL' or 'BLX' */ && disassembly_ins_is_bl_blx(pc - sizeof(size_t)) && (depth < size)) { /* the second depth function may be already saved, so need ignore repeat */ diff --git a/cm_backtrace/cmb_cfg.h b/cm_backtrace/cmb_cfg.h index a269cb5..6875155 100644 --- a/cm_backtrace/cmb_cfg.h +++ b/cm_backtrace/cmb_cfg.h @@ -44,6 +44,13 @@ #define CMB_CPU_PLATFORM_TYPE /* CMB_CPU_ARM_CORTEX_M0 or CMB_CPU_ARM_CORTEX_M3 or CMB_CPU_ARM_CORTEX_M4 or CMB_CPU_ARM_CORTEX_M7 or CMB_CPU_ARM_CORTEX_M33 */ /* enable dump stack information */ /* #define CMB_USING_DUMP_STACK_INFO */ +/* enable user code section backtrace: user-defined code regions beyond Flash */ +/* #define CMB_USING_USER_CODE_SECTION */ +/* additional code regions: a const array initializer of {start, end} pairs, only + used when the above switch is ON. Flash (CMB_CODE_SECTION_NAME) is always + auto-detected. + e.g. ITCM RAM code: #define CMB_USER_CODE_SECTIONS { {0x00000008, 0x0000FFFF} } */ +/* #define CMB_USER_CODE_SECTIONS */ /* language of print information */ /* #define CMB_PRINT_LANGUAGE CMB_PRINT_LANGUAGE_ENGLISH(default) or CMB_PRINT_LANGUAGE_CHINESE or CMB_PRINT_LANGUAGE_CHINESE_UTF8 */ #endif diff --git a/cm_backtrace/cmb_def.h b/cm_backtrace/cmb_def.h index 232b6e5..939ab69 100644 --- a/cm_backtrace/cmb_def.h +++ b/cm_backtrace/cmb_def.h @@ -117,6 +117,11 @@ #define CMB_DUMP_STACK_DEPTH_SIZE (16) #endif +/* max number of code regions in the backtrace region table (Flash + user-defined), default is 8 */ +#ifndef CMB_CODE_REGION_MAX +#define CMB_CODE_REGION_MAX 8 +#endif + /* system handler control and state register */ #ifndef CMB_SYSHND_CTRL #define CMB_SYSHND_CTRL (*(volatile unsigned int*) (0xE000ED24u))