Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions include/rtthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,18 @@ rt_err_t rt_backtrace_formatted_print(rt_ubase_t *buffer, long buflen);
rt_err_t rt_backtrace_to_buffer(rt_thread_t thread, struct rt_hw_backtrace_frame *frame,
long skip, rt_ubase_t *buffer, long buflen);

#ifdef RT_USING_KSYMS
struct rt_ksym_info
{
const char *name;
rt_uintptr_t start;
rt_size_t size;
rt_size_t offset;
};

rt_err_t rt_ksym_lookup(rt_uintptr_t addr, struct rt_ksym_info *info);
#endif /* RT_USING_KSYMS */

#if defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE)
rt_device_t rt_console_set_device(const char *name);
rt_device_t rt_console_get_device(void);
Expand Down
26 changes: 24 additions & 2 deletions libcpu/arm/cortex-a/backtrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
return ret;
}

#ifdef RT_BACKTRACE_FUNCTION_NAME
#if defined(RT_BACKTRACE_FUNCTION_NAME) && !defined(RT_USING_KSYMS)
static char *unwind_get_function_name(void *address)
{
uint32_t flag_word = *(uint32_t *)((char*)address - 4);
Expand Down Expand Up @@ -400,7 +400,7 @@ int unwind_frame(struct stackframe *frame, const struct unwind_idx **origin_idx,
return -URC_FAILURE;
}

#ifdef RT_BACKTRACE_FUNCTION_NAME
#if defined(RT_BACKTRACE_FUNCTION_NAME) && !defined(RT_USING_KSYMS)
{
char *fun_name;
fun_name = unwind_get_function_name((void *)prel31_to_addr(&idx->addr_offset));
Expand Down Expand Up @@ -488,6 +488,27 @@ void unwind_backtrace(struct pt_regs *regs, const struct unwind_idx exidx_start[

arm_get_current_stackframe(regs, &frame);

#ifdef RT_USING_KSYMS
{
rt_ubase_t buffer[RT_BACKTRACE_LEVEL_MAX_NR];
long count = 0;

if (count < RT_BACKTRACE_LEVEL_MAX_NR)
buffer[count++] = frame.pc;

while (count < RT_BACKTRACE_LEVEL_MAX_NR)
{
int urc = unwind_frame(&frame, &origin_idx,
exidx_start, exidx_end);
if (urc < 0)
break;
buffer[count++] = frame.pc;
LOG_D("from: pc = %08x, frame = %08x", frame.pc, frame.sp - 4);
}

rt_backtrace_formatted_print(buffer, count);
}
#else
#ifndef RT_BACKTRACE_FUNCTION_NAME
rt_kprintf("please use: addr2line -e rtthread.elf -a -f %08x\n", frame.pc);
#endif
Expand All @@ -507,6 +528,7 @@ void unwind_backtrace(struct pt_regs *regs, const struct unwind_idx exidx_start[
LOG_D("from: pc = %08x, frame = %08x", frame.pc, frame.sp - 4);
}
rt_kprintf("\n");
#endif
}

extern const struct unwind_idx __exidx_start[];
Expand Down
13 changes: 13 additions & 0 deletions src/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -477,4 +477,17 @@ config RT_BACKTRACE_LEVEL_MAX_NR
int "Max number of backtrace level"
default 32

config RT_USING_KSYMS
bool "Enable kernel symbol table"
default n
help
Embed a compact GCC/ELF kernel function symbol table and use it to
resolve addresses printed by the kernel backtrace API.

This option is supported by the native SCons GCC/ELF build only.
Enabling it with another toolchain is a build error.

The symbol table consumes read-only memory. It is generated during
the GCC build and includes local, global, and weak functions.

endmenu
29 changes: 29 additions & 0 deletions src/kservice.c
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,23 @@ rt_weak rt_err_t rt_backtrace(void)
return rt_backtrace_frame(thread, &frame);
}

#ifdef RT_USING_KSYMS
static void _rt_backtrace_print_pc(rt_ubase_t pc)
{
struct rt_ksym_info info;

rt_kprintf(" 0x%lx", (unsigned long)pc);
if (rt_ksym_lookup(pc, &info) == RT_EOK)
{
rt_kprintf(" <%s+0x%lx", info.name,
(unsigned long)info.offset);
if (info.size != 0)
rt_kprintf("/0x%lx", (unsigned long)info.size);
rt_kprintf(">");
}
}
#endif /* RT_USING_KSYMS */

/**
* @brief Print backtrace from frame to system console device
*
Expand All @@ -455,11 +472,17 @@ rt_weak rt_err_t rt_backtrace_frame(rt_thread_t thread, struct rt_hw_backtrace_f
{
long nesting = 0;

#ifndef RT_USING_KSYMS
rt_kprintf("please use: addr2line -e rtthread.elf -a -f\n");
#endif

while (nesting < RT_BACKTRACE_LEVEL_MAX_NR)
{
#ifdef RT_USING_KSYMS
_rt_backtrace_print_pc((rt_ubase_t)frame->pc);
#else
rt_kprintf(" 0x%lx", (rt_ubase_t)frame->pc);
#endif
if (rt_hw_backtrace_frame_unwind(thread, frame))
{
break;
Expand All @@ -479,11 +502,17 @@ rt_weak rt_err_t rt_backtrace_frame(rt_thread_t thread, struct rt_hw_backtrace_f
*/
rt_weak rt_err_t rt_backtrace_formatted_print(rt_ubase_t *buffer, long buflen)
{
#ifndef RT_USING_KSYMS
rt_kprintf("please use: addr2line -e rtthread.elf -a -f\n");
#endif

for (rt_size_t i = 0; i < buflen && buffer[i] != 0; i++)
{
#ifdef RT_USING_KSYMS
_rt_backtrace_print_pc((rt_ubase_t)buffer[i]);
#else
rt_kprintf(" 0x%lx", (rt_ubase_t)buffer[i]);
#endif
}

rt_kprintf("\n");
Expand Down
87 changes: 87 additions & 0 deletions src/ksym.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <rtthread.h>

#ifdef RT_USING_KSYMS

/* These objects are replaced by the generated table when using GCC builds. */
rt_weak volatile const rt_uintptr_t rt_ksym_first_addr = 0;
rt_weak volatile const rt_uint32_t rt_ksym_count = 0;
rt_weak volatile const rt_uint32_t rt_ksym_entries[1][3] = {{0, 0, 0}};
rt_weak const char rt_ksym_names[1] = "";

static rt_uintptr_t _rt_ksym_normalize_addr(rt_uintptr_t addr)
{
#if defined(ARCH_ARM) && !defined(ARCH_CPU_64BIT)
return addr & ~((rt_uintptr_t)1);
#else
return addr;
#endif
}

static rt_uintptr_t _rt_ksym_entry_addr(rt_uint32_t index)
{
return rt_ksym_first_addr + rt_ksym_entries[index][0];
}

rt_err_t rt_ksym_lookup(rt_uintptr_t addr, struct rt_ksym_info *info)
{
rt_uint32_t low;
rt_uint32_t high;
rt_uintptr_t start;
rt_uint32_t size;

if (!info)
return -RT_EINVAL;
if (rt_ksym_count == 0)
return -RT_ENOSYS;

addr = _rt_ksym_normalize_addr(addr);

if (addr < rt_ksym_first_addr ||
addr - rt_ksym_first_addr > 0xffffffffu)
return -RT_ENOENT;

low = 0;
high = rt_ksym_count;
while (high - low > 1)
{
rt_uint32_t middle = low + (high - low) / 2;

if (_rt_ksym_entry_addr(middle) <= addr)
low = middle;
else
high = middle;
}

if (_rt_ksym_entry_addr(low) > addr)
return -RT_ENOENT;

/* Keep aliases together even if a hand-built table contains them. */
while (low > 0 &&
_rt_ksym_entry_addr(low - 1) == _rt_ksym_entry_addr(low))
{
low--;
}

start = _rt_ksym_entry_addr(low);
size = rt_ksym_entries[low][2];

if (size == 0 && addr != start)
return -RT_ENOENT;
if (size != 0 && addr - start >= size)
return -RT_ENOENT;

info->name = &rt_ksym_names[rt_ksym_entries[low][1]];
info->start = start;
info->offset = addr - start;
info->size = size;

return RT_EOK;
}

#endif /* RT_USING_KSYMS */
5 changes: 5 additions & 0 deletions src/utest/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ menu "Kernel Core"
bool "MT-Safe Kprint Test"
default n

config RT_UTEST_KSYMS
bool "Kernel Symbol Table Test"
default n
depends on RT_USING_KSYMS

config RT_UTEST_SCHEDULER
bool "Scheduler Test"
default n
Expand Down
4 changes: 3 additions & 1 deletion src/utest/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ if GetDepend(['RT_UTEST_HOOKLIST']):
if GetDepend(['RT_UTEST_MTSAFE_KPRINT']):
src += ['mtsafe_kprint_tc.c']

if GetDepend(['RT_UTEST_KSYMS']):
src += ['ksym_tc.c']

if GetDepend(['RT_UTEST_MEMPOOL']):
src += ['mempool_tc.c']

Expand All @@ -74,4 +77,3 @@ for item in list:
group = group + SConscript(os.path.join(item, 'SConscript'))

Return('group')

84 changes: 84 additions & 0 deletions src/utest/ksym_tc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <rtthread.h>
#include "utest.h"
#include "utest_assert.h"

static void __attribute__((noinline)) ksym_test_level1(void);
static void __attribute__((noinline)) ksym_test_level2(void);
static void __attribute__((noinline)) ksym_test_level3(void);

static void __attribute__((noinline)) ksym_test_level1(void)
{
ksym_test_level2();
}

static void __attribute__((noinline)) ksym_test_level2(void)
{
ksym_test_level3();
}

static void __attribute__((noinline)) ksym_test_level3(void)
{
rt_backtrace();
}

static void test_ksym_lookup(void)
{
struct rt_ksym_info info;
rt_uintptr_t address;

address = (rt_uintptr_t)(void *)ksym_test_level1;
uassert_int_equal(rt_ksym_lookup(address, &info), RT_EOK);
uassert_str_equal(info.name, "ksym_test_level1");
uassert_int_equal(info.start,
#if defined(ARCH_ARM) && !defined(ARCH_CPU_64BIT)
address & ~((rt_uintptr_t)1)
#else
address
#endif
);
uassert_int_equal(info.offset, 0);

#if defined(ARCH_ARM) && !defined(ARCH_CPU_64BIT)
uassert_int_equal(rt_ksym_lookup(address | 1, &info), RT_EOK);
uassert_int_equal(info.offset, 0);
#endif

uassert_int_equal(rt_ksym_lookup((rt_uintptr_t)-1, &info), -RT_ENOENT);
uassert_int_equal(rt_ksym_lookup(address, RT_NULL), -RT_EINVAL);
}

static void test_ksym_static_chain(void)
{
struct rt_ksym_info info;

#if defined(ARCH_ARM_CORTEX_A) || defined(ARCH_ARMV8)
ksym_test_level1();
#endif
uassert_int_equal(rt_ksym_lookup((rt_uintptr_t)(void *)ksym_test_level2,
&info), RT_EOK);
uassert_str_equal(info.name, "ksym_test_level2");
}

static rt_err_t utest_tc_init(void)
{
return RT_EOK;
}

static rt_err_t utest_tc_cleanup(void)
{
return RT_EOK;
}

static void testcase(void)
{
UTEST_UNIT_RUN(test_ksym_lookup);
UTEST_UNIT_RUN(test_ksym_static_chain);
}

UTEST_TC_EXPORT(testcase, "core.ksym", utest_tc_init, utest_tc_cleanup, 10);
13 changes: 13 additions & 0 deletions tools/building.py
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,14 @@ def local_group(group, objects):
PreBuilding()
objects = one_list(objects)

if GetDepend('RT_USING_KSYMS'):
if GetOption('target'):
raise RuntimeError('RT_USING_KSYMS is supported by native SCons builds only')
if rtconfig.PLATFORM != 'gcc':
raise RuntimeError('RT_USING_KSYMS requires the GCC/ELF toolchain')
if not getattr(rtconfig, 'OBJDUMP', None):
raise RuntimeError('RT_USING_KSYMS requires rtconfig.OBJDUMP')

program = None
# check whether special buildlib option
lib_name = GetOption('buildlib')
Expand Down Expand Up @@ -918,6 +926,11 @@ def local_group(group, objects):
objects = sorted(objects)
objects.append(objects_in_group)

if GetDepend('RT_USING_KSYMS') and rtconfig.PLATFORM == 'gcc':
from ksym import BuildWithKSym
program = BuildWithKSym(Env, target, objects, EndBuilding)
return

program = Env.Program(target, objects)

EndBuilding(target, program)
Expand Down
Loading
Loading