@@ -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, \
new file mode 100644
@@ -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);
+}
@@ -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
new file mode 100644
@@ -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 */
+}
@@ -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
@@ -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
@@ -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
@@ -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
*