backtrace: sandbox: Add support for obtaining symbols

Add backtrace functions for sandbox, including:

- os_backtrace() to collect addresses into a caller-supplied buffer
- os_backtrace_symbols() to convert addresses to symbol strings
- os_backtrace_symbols_free() to free the symbol array

The libbacktrace library (bundled with GCC) reads DWARF debug information
to provide detailed symbol resolution including function names (even for
static functions), source file paths, and line numbers.

The sandbox backtrace implementation wraps these OS functions to implement
the generic backtrace API (backtrace_init, backtrace_get_syms, etc.).

Enable it for just the 'sandbox' board. Add the library for the Rust
example too.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
This commit is contained in:
Simon Glass
2025-11-28 05:25:55 -07:00
committed by Simon Glass
parent 797feb61d0
commit d5e3d066c7
8 changed files with 239 additions and 2 deletions

View File

@@ -15,9 +15,14 @@ extra-y := start.o
extra-$(CONFIG_SANDBOX_SDL) += sdl.o
obj-$(CONFIG_XPL_BUILD) += spl.o
obj-$(CONFIG_ETH_SANDBOX_RAW) += eth-raw-os.o
obj-$(CONFIG_BACKTRACE) += backtrace.o
# Compile these files with system headers
CFLAGS_USE_SYSHDRS := eth-raw-os.o fuzz.o main.o os.o sdl.o tty.o
CFLAGS_USE_SYSHDRS := backtrace.o eth-raw-os.o fuzz.o main.o os.o sdl.o tty.o
# backtrace.c needs libbacktrace header from GCC
LIBBT_INC := $(dir $(shell $(CC) -print-file-name=include/backtrace.h))
CFLAGS_backtrace.o += -isystem $(LIBBT_INC)
# sdl.c fails to build with -fshort-wchar using musl
cmd_cc_sdl.o = $(CC) $(filter-out -nostdinc -fshort-wchar, \

View File

@@ -0,0 +1,122 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* OS-level backtrace support for sandbox
*
* Copyright 2025 Canonical Ltd
* Written by Simon Glass <simon.glass@canonical.com>
*/
#define _GNU_SOURCE
#include <backtrace.h>
#include <execinfo.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <os.h>
/* libbacktrace state - created once and cached */
static struct backtrace_state *bt_state;
/* Context for collecting symbol info */
struct bt_sym_ctx {
char *buf;
size_t size;
int found;
};
uint os_backtrace(void **buffer, uint size, uint skip)
{
void *tmp[size + skip];
uint count;
int nptrs;
nptrs = backtrace(tmp, size + skip);
if ((int)skip >= nptrs)
return 0;
count = nptrs - skip;
memcpy(buffer, tmp + skip, count * sizeof(*buffer));
return count;
}
static void bt_error_callback(void *data, const char *msg, int errnum)
{
/* Silently ignore errors - we'll fall back to addresses */
}
static struct backtrace_state *get_bt_state(void)
{
if (!bt_state)
bt_state = backtrace_create_state(NULL, 0, bt_error_callback,
NULL);
return bt_state;
}
static int bt_full_callback(void *data, uintptr_t pc, const char *fname,
int lineno, const char *func)
{
struct bt_sym_ctx *ctx = data;
if (func) {
if (fname && lineno)
snprintf(ctx->buf, ctx->size, "%s() at %s:%d", func,
fname, lineno);
else if (fname)
snprintf(ctx->buf, ctx->size, "%s() at %s", func,
fname);
else
snprintf(ctx->buf, ctx->size, "%s()", func);
ctx->found = 1;
}
return 0; /* continue to get innermost frame for inlined functions */
}
char **os_backtrace_symbols(void *const *buffer, uint count)
{
struct backtrace_state *state;
char *str_storage;
char **strings;
uint i;
state = get_bt_state();
/* Allocate array of string pointers plus space for strings */
strings = malloc(count * sizeof(char *) + count * 256);
if (!strings)
return NULL;
/* String storage starts after the pointer array */
str_storage = (char *)(strings + count);
for (i = 0; i < count; i++) {
struct bt_sym_ctx ctx;
strings[i] = str_storage + i * 256;
ctx.buf = strings[i];
ctx.size = 256;
ctx.found = 0;
if (state) {
backtrace_pcinfo(state, (uintptr_t)buffer[i],
bt_full_callback, bt_error_callback,
&ctx);
}
/* Fall back to address if no symbol found */
if (!ctx.found)
snprintf(strings[i], 256, "%p", buffer[i]);
}
return strings;
}
void os_backtrace_symbols_free(char **strings)
{
free(strings);
}

View File

@@ -5,6 +5,7 @@
# (C) Copyright 2002-2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
obj-$(CONFIG_BACKTRACE) += backtrace.o
obj-y += fdt_fixup.o interrupts.o
obj-$(CONFIG_PCI) += pci_io.o
obj-$(CONFIG_BOOT) += bootm.o

View File

@@ -0,0 +1,71 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Backtrace support for sandbox
*
* Copyright 2025 Canonical Ltd
* Written by Simon Glass <simon.glass@canonical.com>
*/
#include <backtrace.h>
#include <errno.h>
#include <os.h>
#include <string.h>
int backtrace_init(struct backtrace_ctx *ctx, uint skip)
{
uint i;
for (i = 0; i < BACKTRACE_MAX; i++)
ctx->syms[i] = NULL;
/* +1 to skip this function */
ctx->count = os_backtrace(ctx->addrs, BACKTRACE_MAX, skip + 1);
return ctx->count;
}
int backtrace_get_syms(struct backtrace_ctx *ctx, char *buf, int size)
{
char **raw_syms;
size_t total_len;
char *p;
uint i;
raw_syms = os_backtrace_symbols(ctx->addrs, ctx->count);
if (!raw_syms)
return -ENOMEM;
/* Calculate total buffer size needed */
total_len = 0;
for (i = 0; i < ctx->count; i++) {
if (raw_syms[i])
total_len += strlen(raw_syms[i]) + 1;
else
total_len += 1; /* empty string */
}
if ((size_t)size < total_len) {
os_backtrace_symbols_free(raw_syms);
return -ENOSPC;
}
/* Copy strings into buffer */
p = buf;
for (i = 0; i < ctx->count; i++) {
ctx->syms[i] = p;
if (raw_syms[i]) {
strcpy(p, raw_syms[i]);
p += strlen(raw_syms[i]) + 1;
} else {
*p++ = '\0';
}
}
os_backtrace_symbols_free(raw_syms);
return 0;
}
void backtrace_uninit(struct backtrace_ctx *ctx)
{
/* Nothing to free - caller owns the buffer */
}

View File

@@ -368,6 +368,7 @@ CONFIG_TPM=y
CONFIG_ERRNO_STR=y
CONFIG_GETOPT=y
CONFIG_ARGON2=y
CONFIG_BACKTRACE=y
CONFIG_TEST_FDTDEC=y
CONFIG_UNIT_TEST=y
CONFIG_UT_TIME=y

View File

@@ -65,6 +65,9 @@ fn main() {
// Fallback to just SDL2 if sdl2-config is not available
println!("cargo:rustc-link-arg=-lSDL2");
}
// Link with libbacktrace for backtrace support on sandbox
println!("cargo:rustc-link-arg=-lbacktrace");
}
// For dynamic linking, link required system libraries normally

View File

@@ -44,4 +44,4 @@ SHARED_LDFLAGS := -L$(UBOOT_BUILD) -lu-boot -Wl,-rpath,$(UBOOT_BUILD)
STATIC_LDFLAGS := -Wl,-T,$(LIB_STATIC_LDS) \
-Wl,--whole-archive $(UBOOT_BUILD)/libu-boot.a \
-Wl,--no-whole-archive \
-lpthread -ldl $(PLATFORM_LIBS) -Wl,-z,noexecstack
-lpthread -ldl -lbacktrace $(PLATFORM_LIBS) -Wl,-z,noexecstack

View File

@@ -576,6 +576,40 @@ int os_setup_signal_handlers(void);
*/
void os_signal_action(int sig, unsigned long pc);
/**
* os_backtrace() - get backtrace addresses
*
* Collect backtrace addresses into a caller-supplied buffer.
*
* @buffer: array to fill with return addresses
* @size: maximum number of entries in buffer
* @skip: number of stack frames to skip (0 to include os_backtrace itself)
* Return: number of addresses collected
*/
uint os_backtrace(void **buffer, uint size, uint skip);
/**
* os_backtrace_symbols() - convert addresses to symbol strings
*
* Convert backtrace addresses to human-readable symbol strings. The returned
* array and strings are allocated with malloc() and must be freed with
* os_backtrace_symbols_free().
*
* @buffer: array of addresses from os_backtrace()
* @count: number of addresses in buffer
* Return: array of symbol strings, or NULL on error
*/
char **os_backtrace_symbols(void *const *buffer, uint count);
/**
* os_backtrace_symbols_free() - free symbol strings
*
* Free the array returned by os_backtrace_symbols().
*
* @strings: array to free (may be NULL)
*/
void os_backtrace_symbols_free(char **strings);
/**
* os_get_time_offset() - get time offset
*