diff --git a/repos/dde_linux/src/lib/lx_emul/debug.cc b/repos/dde_linux/src/lib/lx_emul/debug.cc
index 6116fe4e2c..e10eba74a6 100644
--- a/repos/dde_linux/src/lib/lx_emul/debug.cc
+++ b/repos/dde_linux/src/lib/lx_emul/debug.cc
@@ -23,7 +23,6 @@ using namespace Genode;
extern "C" void lx_emul_trace_and_stop(const char * func)
{
error("Function ", func, " not implemented yet!");
- log("Backtrace follows:");
backtrace();
log("Will sleep forever...");
sleep_forever();
diff --git a/repos/os/include/os/backtrace.h b/repos/os/include/os/backtrace.h
new file mode 100644
index 0000000000..9a244d0abf
--- /dev/null
+++ b/repos/os/include/os/backtrace.h
@@ -0,0 +1,91 @@
+/*
+ * \brief Frame-pointer-based backtrace utility
+ * \author Christian Helmuth
+ * \date 2023-12-14
+ *
+ * To use this utility compile your code with the -fno-omit-frame-pointer GCC
+ * option.
+ */
+
+/*
+ * Copyright (C) 2023 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _INCLUDE__OS__BACKTRACE_H_
+#define _INCLUDE__OS__BACKTRACE_H_
+
+#include
+#include
+#include
+#include
+
+
+namespace Genode {
+ void for_each_return_address(auto const &);
+ void for_each_return_address(Const_byte_range_ptr const &, auto const &);
+
+ struct Backtrace;
+
+ void inline backtrace() __attribute__((always_inline));
+}
+
+#include /* for_each_return_address(fn, stack) */
+
+
+/**
+ * Walk backtrace and call fn() per step
+ *
+ * The walk is limited to the memory of the current thread's stack to prevent
+ * access outside of mapped memory regions. fn() is passed a pointer to the
+ * stack location of the return address.
+ */
+void Genode::for_each_return_address(auto const &fn)
+{
+ Thread::Stack_info const si { Thread::mystack() };
+ Const_byte_range_ptr const stack { (char const *)si.base, si.top - si.base };
+
+ for_each_return_address(stack, fn);
+}
+
+
+/**
+ * Printable backtrace for Genode::log(), Genode::trace(), etc.
+ */
+struct Genode::Backtrace
+{
+ void print(Output &out) const
+ {
+ using Genode::print;
+
+ print(out, "backtrace \"", Thread::myself()->name(), "\"");
+
+ struct Addr : Hex { Addr(void *v) : Hex((addr_t)v, OMIT_PREFIX) { } };
+
+ unsigned width = 0;
+ for_each_return_address([&] (void **p) {
+ width = max(width, printed_length(Addr(*p)));
+ });
+ if (!width) {
+ print(out, "\n ");
+ return;
+ }
+
+ for_each_return_address([&] (void **p) {
+ print(out, "\n ", Addr(p), " ", Right_aligned(width, Addr(*p)));
+ });
+ }
+};
+
+
+/**
+ * Print backtrace via Genode::log()
+ */
+void inline Genode::backtrace()
+{
+ Genode::log(Backtrace());
+}
+
+#endif /* _INCLUDE__OS__BACKTRACE_H_ */
diff --git a/repos/os/include/spec/arm/os/backtrace.h b/repos/os/include/spec/arm/os/backtrace.h
deleted file mode 100644
index 99865f384d..0000000000
--- a/repos/os/include/spec/arm/os/backtrace.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * \brief Backtrace helper utility
- * \date 2015-09-18
- * \author Christian Prochaska
- * \author Stefan Kalkowski
- */
-
-/*
- * Copyright (C) 2015-2017 Genode Labs GmbH
- *
- * This file is part of the Genode OS framework, which is distributed
- * under the terms of the GNU Affero General Public License version 3.
- */
-
-#ifndef _INCLUDE__SPEC__ARM__OS__BACKTRACE_H_
-#define _INCLUDE__SPEC__ARM__OS__BACKTRACE_H_
-
-#include
-
-namespace Genode { void inline backtrace() __attribute__((always_inline)); }
-
-/**
- * Print frame pointer based backtrace
- *
- * To use this function compile your code with the -fno-omit-frame-pointer GCC
- * option.
- */
-void inline Genode::backtrace()
-{
- addr_t * fp;
-
- asm volatile ("mov %0, %%fp" : "=r"(fp) : :);
-
- while (fp && *fp) {
- Genode::log(Hex(*fp));
- fp = (addr_t*)*(fp - 1);
- }
-}
-
-#endif /* _INCLUDE__SPEC__ARM__OS__BACKTRACE_H_ */
diff --git a/repos/os/include/spec/arm/os/for_each_return_address.h b/repos/os/include/spec/arm/os/for_each_return_address.h
new file mode 100644
index 0000000000..858e6a0f4a
--- /dev/null
+++ b/repos/os/include/spec/arm/os/for_each_return_address.h
@@ -0,0 +1,33 @@
+/*
+ * \brief Backtrace helper utility (arm_v6/v7a)
+ * \author Christian Prochaska
+ * \author Stefan Kalkowski
+ * \author Christian Helmuth
+ * \date 2015-09-18
+ */
+
+/*
+ * Copyright (C) 2015-2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _INCLUDE__SPEC__ARM__OS__FOR_EACH_RETURN_ADDRESS_H_
+#define _INCLUDE__SPEC__ARM__OS__FOR_EACH_RETURN_ADDRESS_H_
+
+/* included from os/backtrace.h */
+
+void Genode::for_each_return_address(Const_byte_range_ptr const &stack, auto const &fn)
+{
+ void **fp;
+
+ asm volatile ("mov %0, %%fp" : "=r"(fp) : :);
+
+ while (stack.contains(fp - 1) && stack.contains(fp) && fp[0]) {
+ fn(fp);
+ fp = (void **) fp[-1];
+ }
+}
+
+#endif /* _INCLUDE__SPEC__ARM__OS__FOR_EACH_RETURN_ADDRESS_H_ */
diff --git a/repos/os/include/spec/arm_64/os/backtrace.h b/repos/os/include/spec/arm_64/os/backtrace.h
deleted file mode 100644
index 2f66a83b2a..0000000000
--- a/repos/os/include/spec/arm_64/os/backtrace.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * \brief Backtrace helper utility
- * \date 2020-01-21
- * \author Stefan Kalkowski
- */
-
-/*
- * Copyright (C) 2020 Genode Labs GmbH
- *
- * This file is part of the Genode OS framework, which is distributed
- * under the terms of the GNU Affero General Public License version 3.
- */
-
-#ifndef _INCLUDE__SPEC__ARM_64__OS__BACKTRACE_H_
-#define _INCLUDE__SPEC__ARM_64__OS__BACKTRACE_H_
-
-#include
-
-namespace Genode { void inline backtrace() __attribute__((always_inline)); }
-
-/**
- * Print frame pointer based backtrace
- *
- * To use this function compile your code with the -fno-omit-frame-pointer GCC
- * option.
- */
-void inline Genode::backtrace()
-{
- addr_t * fp;
-
- asm volatile ("mov %0, x29" : "=r"(fp) ::);
-
- while (fp) {
- addr_t ip = fp[1];
- fp = (addr_t*) fp[0];
- Genode::log(Hex(ip));
- }
-}
-
-#endif /* _INCLUDE__SPEC__ARM_64__OS__BACKTRACE_H_ */
-
diff --git a/repos/os/include/spec/arm_64/os/for_each_return_address.h b/repos/os/include/spec/arm_64/os/for_each_return_address.h
new file mode 100644
index 0000000000..f9e8b159fd
--- /dev/null
+++ b/repos/os/include/spec/arm_64/os/for_each_return_address.h
@@ -0,0 +1,32 @@
+/*
+ * \brief Backtrace helper utility (arm_v8a)
+ * \author Stefan Kalkowski
+ * \author Christian Helmuth
+ * \date 2020-01-21
+ */
+
+/*
+ * Copyright (C) 2020 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _INCLUDE__SPEC__ARM_64__OS__FOR_EACH_RETURN_ADDRESS_H_
+#define _INCLUDE__SPEC__ARM_64__OS__FOR_EACH_RETURN_ADDRESS_H_
+
+/* included from os/backtrace.h */
+
+void Genode::for_each_return_address(Const_byte_range_ptr const &stack, auto const &fn)
+{
+ void **fp;
+
+ asm volatile ("mov %0, x29" : "=r"(fp) ::);
+
+ while (stack.contains(fp) && stack.contains(fp + 1) && fp[1]) {
+ fn(fp + 1);
+ fp = (void **) fp[0];
+ }
+}
+
+#endif /* _INCLUDE__SPEC__ARM_64__OS__FOR_EACH_RETURN_ADDRESS_H_ */
diff --git a/repos/os/include/spec/x86_32/os/backtrace.h b/repos/os/include/spec/x86_32/os/backtrace.h
deleted file mode 100644
index e6c930cbc1..0000000000
--- a/repos/os/include/spec/x86_32/os/backtrace.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * \brief Backtrace helper utility
- * \date 2015-09-18
- * \author Christian Prochaska
- * \author Stefan Kalkowski
- */
-
-/*
- * Copyright (C) 2015-2017 Genode Labs GmbH
- *
- * This file is part of the Genode OS framework, which is distributed
- * under the terms of the GNU Affero General Public License version 3.
- */
-
-#ifndef _INCLUDE__SPEC__X86_32__OS__BACKTRACE_H_
-#define _INCLUDE__SPEC__X86_32__OS__BACKTRACE_H_
-
-#include
-#include
-
-namespace Genode { void inline backtrace() __attribute__((always_inline)); }
-
-/**
- * Print frame pointer based backtrace
- *
- * To use this function compile your code with the -fno-omit-frame-pointer GCC
- * option.
- */
-void inline Genode::backtrace()
-{
- Genode::addr_t * fp;
-
- asm volatile ("movl %%ebp, %0" : "=r"(fp) : :);
-
- while (fp && *(fp + 1)) {
- Genode::log(Hex(*(fp + 1)));
- fp = (Genode::addr_t*)*fp;
- }
-}
-
-#endif /* _INCLUDE__SPEC__X86_32__OS__BACKTRACE_H_ */
diff --git a/repos/os/include/spec/x86_32/os/for_each_return_address.h b/repos/os/include/spec/x86_32/os/for_each_return_address.h
new file mode 100644
index 0000000000..29d6a6eaaa
--- /dev/null
+++ b/repos/os/include/spec/x86_32/os/for_each_return_address.h
@@ -0,0 +1,33 @@
+/*
+ * \brief Backtrace helper utility (x86_32)
+ * \author Christian Prochaska
+ * \author Stefan Kalkowski
+ * \author Christian Helmuth
+ * \date 2015-09-18
+ */
+
+/*
+ * Copyright (C) 2015-2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _INCLUDE__SPEC__X86_32__OS__FOR_EACH_RETURN_ADDRESS_H_
+#define _INCLUDE__SPEC__X86_32__OS__FOR_EACH_RETURN_ADDRESS_H_
+
+/* included from os/backtrace.h */
+
+void Genode::for_each_return_address(Const_byte_range_ptr const &stack, auto const &fn)
+{
+ void **fp;
+
+ asm volatile ("movl %%ebp, %0" : "=r"(fp) : :);
+
+ while (stack.contains(fp) && stack.contains(fp + 1) && fp[1]) {
+ fn(fp + 1);
+ fp = (void **) fp[0];
+ }
+}
+
+#endif /* _INCLUDE__SPEC__X86_32__OS__FOR_EACH_RETURN_ADDRESS_H_ */
diff --git a/repos/os/include/spec/x86_64/os/backtrace.h b/repos/os/include/spec/x86_64/os/backtrace.h
deleted file mode 100644
index 2e6849f39e..0000000000
--- a/repos/os/include/spec/x86_64/os/backtrace.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * \brief Backtrace helper utility
- * \date 2015-09-18
- * \author Christian Prochaska
- * \author Stefan Kalkowski
- */
-
-/*
- * Copyright (C) 2015-2017 Genode Labs GmbH
- *
- * This file is part of the Genode OS framework, which is distributed
- * under the terms of the GNU Affero General Public License version 3.
- */
-
-#ifndef _INCLUDE__SPEC__X86_64__OS__BACKTRACE_H_
-#define _INCLUDE__SPEC__X86_64__OS__BACKTRACE_H_
-
-#include
-#include
-
-namespace Genode { void inline backtrace() __attribute__((always_inline)); }
-
-/**
- * Print frame pointer based backtrace
- *
- * To use this function compile your code with the -fno-omit-frame-pointer GCC
- * option.
- */
-void inline Genode::backtrace()
-{
- Genode::addr_t * fp;
-
- asm volatile ("movq %%rbp, %0" : "=r"(fp) : :);
-
- while (fp && *(fp + 1)) {
- Genode::log(Hex(*(fp + 1)));
- fp = (Genode::addr_t*)*fp;
- }
-}
-
-#endif /* _INCLUDE__SPEC__X86_64__OS__BACKTRACE_H_ */
diff --git a/repos/os/include/spec/x86_64/os/for_each_return_address.h b/repos/os/include/spec/x86_64/os/for_each_return_address.h
new file mode 100644
index 0000000000..5b384bfb52
--- /dev/null
+++ b/repos/os/include/spec/x86_64/os/for_each_return_address.h
@@ -0,0 +1,33 @@
+/*
+ * \brief Backtrace helper utility (x86_64)
+ * \author Christian Prochaska
+ * \author Stefan Kalkowski
+ * \author Christian Helmuth
+ * \date 2015-09-18
+ */
+
+/*
+ * Copyright (C) 2015-2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _INCLUDE__SPEC__X86_64__OS__FOR_EACH_RETURN_ADDRESS_H_
+#define _INCLUDE__SPEC__X86_64__OS__FOR_EACH_RETURN_ADDRESS_H_
+
+/* included from os/backtrace.h */
+
+void Genode::for_each_return_address(Const_byte_range_ptr const &stack, auto const &fn)
+{
+ void **fp;
+
+ asm volatile ("movq %%rbp, %0" : "=r"(fp) : :);
+
+ while (stack.contains(fp) && stack.contains(fp + 1) && fp[1]) {
+ fn(fp + 1);
+ fp = (void **) fp[0];
+ }
+}
+
+#endif /* _INCLUDE__SPEC__X86_64__OS__FOR_EACH_RETURN_ADDRESS_H_ */