[RFC PATCH 1/2] libdwfl_stacktace/dwflst_sample_frame.c: API for per-arch constants
Serhei Makarov
serhei@serhei.io
Thu Aug 6 16:29:46 GMT 2026
One need identified from implementing patches for downstream profiling
tools to adopt libdwfl_stacktrace is that libebl per-architecture
constants are not exposed to external projects.
TODO(REVIEW): First iteration of this patch for RFC: the APIs are part
of libdwfl_stacktrace, and return error values on architectures that
lack a sample_regs backend (to signal that libdwflst is not yet
supported there). This is most useful for the profiler use case, but
need to double-check if it makes sense to provide a more general arch
constant API in libdwfl/libdw.
* libdwfl_stacktrace/libdwfl_stacktrace.h
(dwflst_arch_from_uname): New API, translates umachine str obtained
from uname() -> struct utsname -> machine to ELF machine ID.
(dwflst_arch_sp_dwarf_reg): New API, identifies index of stack ptr.
(dwflst_arch_expected_frame_nregs): New API, minimal number of regs
for unwinding (useful for sanity-checking incoming stack samples).
* libdwfl_stacktrace/dwflst_sample_frame.c: Format in two sections.
(dwflst_arch_from_uname): Implementation.
(dwflst_arch_expected_frame_nregs): Implementation.
(dwflst_arch_sp_dwarf_reg): Implementation.
* libdw/libdw.map (ELFUTILS_0.196_EXPERIMENTAL): Add the new functions.
Signed-off-by: Serhei Makarov <serhei@serhei.io>
---
libdw/libdw.map | 7 +++
libdwfl_stacktrace/dwflst_sample_frame.c | 58 +++++++++++++++++++++++-
libdwfl_stacktrace/libdwfl_stacktrace.h | 21 +++++++++
3 files changed, 85 insertions(+), 1 deletion(-)
diff --git a/libdw/libdw.map b/libdw/libdw.map
index b45647e6..f1f8362a 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -411,3 +411,10 @@ ELFUTILS_0.194_EXPERIMENTAL {
global:
dwflst_sample_getframes;
} ELFUTILS_0.193_EXPERIMENTAL;
+
+ELFUTILS_0.196_EXPERIMENTAL {
+ global:
+ dwflst_arch_from_uname;
+ dwflst_arch_expected_frame_nregs;
+ dwflst_arch_sp_dwarf_reg;
+} ELFUTILS_0.194_EXPERIMENTAL;
diff --git a/libdwfl_stacktrace/dwflst_sample_frame.c b/libdwfl_stacktrace/dwflst_sample_frame.c
index cf33a439..0e7d9f62 100644
--- a/libdwfl_stacktrace/dwflst_sample_frame.c
+++ b/libdwfl_stacktrace/dwflst_sample_frame.c
@@ -32,10 +32,13 @@
#include "libdwfl_stacktraceP.h"
+/* Various functions providing arch-specific info: */
+
Ebl *default_ebl = NULL;
GElf_Half default_ebl_machine = EM_NONE;
-uint64_t dwflst_perf_sample_preferred_regs_mask (GElf_Half machine)
+uint64_t
+dwflst_perf_sample_preferred_regs_mask (GElf_Half machine)
{
/* XXX The most likely case is that this will only be called once,
for the current architecture. So we keep one Ebl* around for
@@ -56,6 +59,59 @@ uint64_t dwflst_perf_sample_preferred_regs_mask (GElf_Half machine)
return 0;
}
+GElf_Half
+dwflst_arch_from_uname (const char *umachine)
+{
+ if (strncmp(umachine, "x86_64", 6) == 0)
+ return EM_X86_64;
+ else if (strncmp(umachine, "i686", 4) == 0
+ || strncmp(umachine, "i386", 4) == 0)
+ return EM_386;
+ else if (strncmp(umachine, "aarch64", 7) == 0)
+ return EM_AARCH64;
+ else if (strncmp(umachine, "armv7l", 6) == 0)
+ return EM_ARM;
+ /* XXX Other architectures not yet supported. */
+ return EM_NONE;
+}
+
+uint32_t
+dwflst_arch_expected_frame_nregs (GElf_Half machine)
+{
+ /* For aarch64, we actually use fewer than ebl->frame_nregs to unwind: */
+ if (machine == EM_AARCH64)
+ return 14;
+ if (machine == EM_ARM)
+ return 16;
+ /* On x86, expect everything except FLAGS: */
+ if (machine == EM_X86_64 || machine == EM_386)
+ /* XXX An external user of the library can't access the Ebl, hence
+ can't conveniently provide it to us it here. We provide the
+ constant directly rather than initializing a new Ebl. */
+ return machine == EM_X86_64 ? 17 : 9;
+ /* return ebl_frame_nregs(ebl); */
+ /* XXX Other architectures are not supported yet.
+ In general, it's fine to be on the permissive side here. */
+ return 1;
+}
+
+int
+dwflst_arch_sp_dwarf_reg (GElf_Half machine, bool is_abi32)
+{
+ /* Use is_abi32 to downgrade from 64-bit to 32-bit,
+ but not in the other direction since 64-bit samples
+ do not occur on a 32-bit ELF machine: */
+ if (machine == EM_X86_64 || machine == EM_386)
+ return (is_abi32 || machine == EM_386) ? 4 : 7;
+ else if (machine == EM_ARM || machine == EM_AARCH64)
+ return (is_abi32 || machine == EM_ARM) ? 13 : 31;
+ else
+ /* XXX Other architectures are not supported yet. */
+ return -1;
+}
+
+/* Stack sample handling: */
+
struct sample_info {
pid_t pid;
pid_t tid;
diff --git a/libdwfl_stacktrace/libdwfl_stacktrace.h b/libdwfl_stacktrace/libdwfl_stacktrace.h
index 84cb69a3..82689aca 100644
--- a/libdwfl_stacktrace/libdwfl_stacktrace.h
+++ b/libdwfl_stacktrace/libdwfl_stacktrace.h
@@ -154,6 +154,27 @@ int dwflst_perf_sample_getframes (Dwfl *dwfl, Elf *elf, pid_t pid, pid_t tid,
opened later. */
uint64_t dwflst_perf_sample_preferred_regs_mask (GElf_Half machine);
+/* Returns the correct ELF machine identifier for the arch identifier
+ string UMACHINE from struct utsname, if libdwflst handles stack
+ samples for MACHINE. Returns EM_NONE if stack sample handling is
+ not supported. */
+GElf_Half dwflst_arch_from_uname (const char *umachine);
+
+/* Returns the minimum required number of registers for unwinding
+ for MACHINE, which should be at most the number of bits set in
+ dwflst_perf_sample_preferred_regs_mask(MACHINE). Returns 0
+ if libdwfl does not handle stack samples for MACHINE. */
+uint32_t dwflst_arch_expected_frame_nregs (GElf_Half machine);
+
+/* Returns the index of the stack pointer register within the
+ dwarf_regs ordering for MACHINE. If IS_ABI32 is true, returns the
+ index within the dwarf_regs ordering for the 32-bit variant of
+ MACHINE, to allow handling of mixed-architecture perf_events data.
+ (When providing data from non-perf_events sources, IS_ABI32 should
+ be set to false.) Returns -1 if libdwfl does not handle stack
+ samples for MACHINE. */
+int dwflst_arch_sp_dwarf_reg (GElf_Half machine, bool is_abi32);
+
#ifdef __cplusplus
}
#endif
--
2.54.0
More information about the Elfutils-devel
mailing list