From patchwork Sat Nov 29 07:59:49 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 771 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1764403258; bh=rIDH3CrK+cktultb87DcB/xM+6Fz3huodHgp1Gk+pnA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=uan6B0GGK0qTcw9hEXm0+0Ur5R1kFeeRMf6P19LIv2pPfrzEB+nSBbXbaZFqzvvRy xVtn5mljT3+SgA7xHw2BSQ0O+GqKtwf08k6YA7g/wmyDOIZkzzFO40iXuEB962FJyu /AFsB5AhZXHEZAaFBM4y7zkdgtWtz680caQYako3hPTrRD/kIDlJb6/10gxiHn8TMA FUKhJctSCvOhxFQIWZt2CUjuqpxKP2zOXv8WQwt/ZAGvw9+Sx9g1yw1txdeVmmKp56 rwNeMTH2gBbKRVQpYKXUpHNZ+cHUhL3Fn0kv7ayNoHJPeyTBI8GLprjmbSD2yd7DOQ 1IDfYVlfsyi3Q== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 41F08687CC for ; Sat, 29 Nov 2025 01:00:58 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id Fxh9nrkpYAla for ; Sat, 29 Nov 2025 01:00:58 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1764403258; bh=rIDH3CrK+cktultb87DcB/xM+6Fz3huodHgp1Gk+pnA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=uan6B0GGK0qTcw9hEXm0+0Ur5R1kFeeRMf6P19LIv2pPfrzEB+nSBbXbaZFqzvvRy xVtn5mljT3+SgA7xHw2BSQ0O+GqKtwf08k6YA7g/wmyDOIZkzzFO40iXuEB962FJyu /AFsB5AhZXHEZAaFBM4y7zkdgtWtz680caQYako3hPTrRD/kIDlJb6/10gxiHn8TMA FUKhJctSCvOhxFQIWZt2CUjuqpxKP2zOXv8WQwt/ZAGvw9+Sx9g1yw1txdeVmmKp56 rwNeMTH2gBbKRVQpYKXUpHNZ+cHUhL3Fn0kv7ayNoHJPeyTBI8GLprjmbSD2yd7DOQ 1IDfYVlfsyi3Q== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 3103D687B5 for ; Sat, 29 Nov 2025 01:00:58 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1764403256; bh=mOf03Mv8EKe8cAvkkXmAYOLFh9DlQDfK4b/sZ/9w9Kk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cYPThkDLgnBcf8CoG4HQRDO5fqBmPjHsI6ZxB915AtZOmeWN733zYlduW6gdaZ2zw 5s35YHBRK1639/X2v+GvjQYFraCOD9beoa/4SHje8EVwSZ0s6mEux+13rerMrEI0YQ WC1BDzP4DpibHgFXYdPa13YpC59zzZolrQYDSrRNWqWpiIMy7LOfeDD1WdUFELgj+z duXXmKId+fNVHDONt//OVTVPbQHj8UIMbKr/6qShoHG3zzYV8t8iVl1hc5okRXLeYo 0HoNPXX/FUSz7hMBww8oqiUmcOwYqDt2SlJ/Fr+wjKFkJ+hi0oy0NVtPf8J75Hh1o9 A9mMkdAEESDfA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 27F6C6875D; Sat, 29 Nov 2025 01:00:56 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id BPPc0NZX0__r; Sat, 29 Nov 2025 01:00:56 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1764403251; bh=D4k14JL6O76TPumUKgpDJZHZTWKHoD5CSQEAJ0z+0jk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RHJAcT4upTdHysJV3ie9GcpGeP2Gz/zpLEx2UEwTpg8ogZaV+b2WQXeUMgS0mFc3k Z1JxQk1igk/r7YHlo9VdZNLQCyTwkd5oC5DG22z+DG4ReHtZzmT1uMtHzTEExSJBtW 0lsLyWwxRPOXUaUTioPEQ4V66lpo4FXPfl9Ywn3up46j4g7oeMazF8Vu4ArX4eMyry P1Gks2Rwy2WTIm0EFFqixbCn1FO84h9qbHChR2EsaJEpDHwXgU0Owb0d65iQ4tAAdW Pu/qNoGHb92Jb6SEwaO6KwAYHZi8ea0M/xwvxP3XhNmMUvCrNE2uhyip3+6UNBJxRH k2Q4SpY15WcqQ== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 7F74A6876F; Sat, 29 Nov 2025 01:00:51 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Sat, 29 Nov 2025 00:59:49 -0700 Message-ID: <20251129080014.758001-5-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251129080014.758001-1-sjg@u-boot.org> References: <20251129080014.758001-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: GHRLNRK642GMP2TLVIQNGHM27CTN6VWD X-Message-ID-Hash: GHRLNRK642GMP2TLVIQNGHM27CTN6VWD X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Heinrich Schuchardt , Simon Glass , Claude X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 04/10] backtrace: sandbox: Add support for obtaining symbols List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass 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 Signed-off-by: Simon Glass --- arch/sandbox/cpu/Makefile | 7 +- arch/sandbox/cpu/backtrace.c | 122 +++++++++++++++++++++++++++++++++++ arch/sandbox/lib/Makefile | 1 + arch/sandbox/lib/backtrace.c | 71 ++++++++++++++++++++ configs/sandbox_defconfig | 1 + examples/rust/build.rs | 3 + examples/ulib/config.mk | 2 +- include/os.h | 34 ++++++++++ 8 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 arch/sandbox/cpu/backtrace.c create mode 100644 arch/sandbox/lib/backtrace.c diff --git a/arch/sandbox/cpu/Makefile b/arch/sandbox/cpu/Makefile index ef9a01c5d7c..212cf14fe37 100644 --- a/arch/sandbox/cpu/Makefile +++ b/arch/sandbox/cpu/Makefile @@ -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, \ diff --git a/arch/sandbox/cpu/backtrace.c b/arch/sandbox/cpu/backtrace.c new file mode 100644 index 00000000000..1f5a14ed541 --- /dev/null +++ b/arch/sandbox/cpu/backtrace.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * OS-level backtrace support for sandbox + * + * Copyright 2025 Canonical Ltd + * Written by Simon Glass + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include + +/* 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); +} diff --git a/arch/sandbox/lib/Makefile b/arch/sandbox/lib/Makefile index edb650c48da..563a5c33156 100644 --- a/arch/sandbox/lib/Makefile +++ b/arch/sandbox/lib/Makefile @@ -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 diff --git a/arch/sandbox/lib/backtrace.c b/arch/sandbox/lib/backtrace.c new file mode 100644 index 00000000000..073eb945622 --- /dev/null +++ b/arch/sandbox/lib/backtrace.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Backtrace support for sandbox + * + * Copyright 2025 Canonical Ltd + * Written by Simon Glass + */ + +#include +#include +#include +#include + +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 */ +} diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 006c6916af6..55f8ddcc952 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -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 diff --git a/examples/rust/build.rs b/examples/rust/build.rs index 9c713ba574c..6be6a21d714 100644 --- a/examples/rust/build.rs +++ b/examples/rust/build.rs @@ -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 diff --git a/examples/ulib/config.mk b/examples/ulib/config.mk index 993f0923b4f..5e2ffa5a4fe 100644 --- a/examples/ulib/config.mk +++ b/examples/ulib/config.mk @@ -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 diff --git a/include/os.h b/include/os.h index 1b2243d46d4..ab4710fc265 100644 --- a/include/os.h +++ b/include/os.h @@ -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 *