You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ARM平台FreeRTOS中dump_stack()替代:栈地址转函数符号需求

Fixing _Unwind_Backtrace Symbol Resolution on ARM + FreeRTOS

Great question — I’ve dealt with this exact problem when debugging FreeRTOS on ARM targets. The core issue is that _Unwind_Backtrace only gives you raw program counter (PC) addresses; it doesn’t have built-in logic to map those addresses to function names, filenames, or line numbers. Let’s walk through two main approaches to solve this: offline debugging (quick for one-off checks) and runtime symbol resolution (for in-app call chain printing).

1. First: Ensure Debug Symbols Are Enabled

Before anything else, you need to compile your code with debug symbols included — otherwise, there’s no data to map addresses to symbols. Update your GCC compile flags to include:

  • -g: Generates debug information (level 2 is default and sufficient)
  • Avoid -s (which strips symbols) and don’t run strip on your final ELF binary

Example compile command (adjust mcpu for your ARM core):

arm-none-eabi-gcc -g -O0 -mcpu=cortex-m4 -mthumb -o your_app your_app.c -lfreertos

2. Offline Address-to-Symbol Resolution (Quick Debugging)

If you just need to debug a crash or call chain after the fact, use the GNU addr2line tool. This is the fastest way to translate PC addresses to human-readable info.

Take a PC address from your output (like 0x40076b) and run:

arm-none-eabi-addr2line -e your_app.elf 0x40076b

You’ll get output like:

/home/you/projects/your_app.c:22

Or with function names if you add -f:

arm-none-eabi-addr2line -f -e your_app.elf 0x40076b

Output:

func_1
/home/you/projects/your_app.c:22

3. Runtime Symbol Resolution (In-App Call Chain Printing)

If you want your FreeRTOS application to print function names directly when calling your stack trace function, you have two options depending on your environment:

Option A: For Linux/Android on ARM (Dynamic Linking Supported)

If you’re running FreeRTOS on top of a Linux kernel (or another OS with dynamic linking), use the dladdr() function from libdl to resolve symbols at runtime. Modify your callback like this:

#include <stdio.h>
#include <unwind.h>
#include <stdint.h>
#include <dlfcn.h>

static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg) {
    uintptr_t pc = _Unwind_GetIP(context);
    if (pc) {
        printf("unwind got pc ...0x%x\n", pc);
        
        Dl_info symbol_info;
        if (dladdr((void*)pc, &symbol_info)) {
            if (symbol_info.dli_sname) {
                printf("  Function: %s\n", symbol_info.dli_sname);
            }
            if (symbol_info.dli_fname) {
                printf("  Source File: %s\n", symbol_info.dli_fname);
            }
        }
    }
    return _URC_NO_REASON;
}

// Rest of your existing code remains the same
ssize_t unwind_backtrace() {
    _Unwind_Reason_Code rc = _Unwind_Backtrace(unwind_backtrace_callback, 0);
    return rc == _URC_END_OF_STACK ? 0 : -1;
}

void func_1() {
    int ret = unwind_backtrace();
    printf("unwind_backtrace return ...%d\n", ret);
}

void func_2() {
    func_1();
}

int main() {
    func_2();
    return 0;
}

Compile with -ldl to link the dynamic linking library:

arm-none-eabi-gcc -g -o your_app your_app.c -ldl

Option B: For Bare-Metal FreeRTOS (No Dynamic Linking)

Bare-metal FreeRTOS targets don’t have a dynamic linker, so we’ll embed the ELF symbol table directly into the binary and parse it manually. Here’s how:

Step 1: Update Your Linker Script

Add these lines to your linker script (e.g., linker.ld) to expose the symbol table and string table addresses:

// Place these inside the .text or .data section (adjust as needed)
PROVIDE(__symtab_start = .);
*(.symtab)
PROVIDE(__symtab_end = .);
PROVIDE(__strtab_start = .);
*(.strtab)
PROVIDE(__strtab_end = .);

Step 2: Modify Your Code to Parse Symbols

Add a function to map PC addresses to function names, then update your callback:

#include <stdio.h>
#include <unwind.h>
#include <stdint.h>
#include <elf.h>

// Import symbols from the linker script
extern Elf32_Sym __symtab_start;
extern Elf32_Sym __symtab_end;
extern char __strtab_start;
extern char __strtab_end;

// Resolve a PC address to its function name
const char* addr_to_symbol(uintptr_t addr) {
    Elf32_Sym* current_sym = &__symtab_start;
    while (current_sym < &__symtab_end) {
        // Only consider function symbols (STT_FUNC)
        if (ELF32_ST_TYPE(current_sym->st_info) == STT_FUNC) {
            uintptr_t sym_start = current_sym->st_value;
            uintptr_t sym_end = sym_start + current_sym->st_size;
            // Check if the PC falls within this function's address range
            if (addr >= sym_start && addr < sym_end) {
                // Grab the symbol name from the string table
                return &__strtab_start + current_sym->st_name;
            }
        }
        current_sym++;
    }
    return "unknown";
}

static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg) {
    uintptr_t pc = _Unwind_GetIP(context);
    if (pc) {
        printf("unwind got pc ...0x%x\n", pc);
        const char* func_name = addr_to_symbol(pc);
        printf("  Function: %s\n", func_name);
    }
    return _URC_NO_REASON;
}

// Rest of your code stays the same
ssize_t unwind_backtrace() {
    _Unwind_Reason_Code rc = _Unwind_Backtrace(unwind_backtrace_callback, 0);
    return rc == _URC_END_OF_STACK ? 0 : -1;
}

void func_1() {
    int ret = unwind_backtrace();
    printf("unwind_backtrace return ...%d\n", ret);
}

void func_2() {
    func_1();
}

int main() {
    func_2();
    return 0;
}

Compile with your modified linker script:

arm-none-eabi-gcc -g -O0 -mcpu=cortex-m4 -mthumb -T linker.ld -o your_app your_app.c

4. FreeRTOS-Specific Notes

  • Task Stack Sizes: Make sure your FreeRTOS task stacks are large enough — stack overflow will corrupt your backtrace data.
  • Interrupt Context: If you’re calling the stack trace from an interrupt, ensure you’re using the correct stack (some ARM cores have separate interrupt stacks) to get accurate call chains.

Example Output

After implementing one of these methods, your output will look like this:

unwind got pc ...0x40076b
  Function: func_1
unwind got pc ...0x400796
  Function: func_2
unwind got pc ...0x4007bd
  Function: main
unwind got pc ...0x400819
  Function: __libc_start_main
unwind got pc ...0x67314b15
  Function: unknown
unwind got pc ...0x400649
  Function: _start
unwind_backtrace return ...0

内容的提问来源于stack exchange,提问作者KSoni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:35:35