From: Weinan Liu <wnliu@google.com>
To: Josh Poimboeuf <jpoimboe@kernel.org>,
Steven Rostedt <rostedt@goodmis.org>,
Indu Bhagat <indu.bhagat@oracle.com>,
Peter Zijlstra <peterz@infradead.org>
Cc: Mark Rutland <mark.rutland@arm.com>,
roman.gushchin@linux.dev, Will Deacon <will@kernel.org>,
Ian Rogers <irogers@google.com>,
linux-toolchains@vger.kernel.org, linux-kernel@vger.kernel.org,
live-patching@vger.kernel.org, joe.lawrence@redhat.com,
linux-arm-kernel@lists.infradead.org,
Weinan Liu <wnliu@google.com>
Subject: [PATCH 4/8] unwind: Implement generic sframe unwinder library
Date: Mon, 27 Jan 2025 21:33:06 +0000 [thread overview]
Message-ID: <20250127213310.2496133-5-wnliu@google.com> (raw)
In-Reply-To: <20250127213310.2496133-1-wnliu@google.com>
This change introduces a kernel space unwinder using sframe table for
architectures without ORC unwinder support.
The implementation is adapted from Josh's userspace sframe unwinder
proposal[1] according to the sframe v2 spec[2].
[1] https://lore.kernel.org/lkml/42c0a99236af65c09c8182e260af7bcf5aa1e158.1730150953.git.jpoimboe@kernel.org/
[2] https://sourceware.org/binutils/docs/sframe-spec.html
Signed-off-by: Weinan Liu <wnliu@google.com>
---
include/linux/sframe_lookup.h | 43 ++++++++
kernel/Makefile | 1 +
kernel/sframe_lookup.c | 196 ++++++++++++++++++++++++++++++++++
3 files changed, 240 insertions(+)
create mode 100644 include/linux/sframe_lookup.h
create mode 100644 kernel/sframe_lookup.c
diff --git a/include/linux/sframe_lookup.h b/include/linux/sframe_lookup.h
new file mode 100644
index 000000000000..1c26cf1f38d4
--- /dev/null
+++ b/include/linux/sframe_lookup.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_SFRAME_LOOKUP_H
+#define _LINUX_SFRAME_LOOKUP_H
+
+/**
+ * struct sframe_ip_entry - sframe unwind info for given ip
+ * @cfa_offset: Offset for the Canonical Frame Address(CFA) from Frame
+ * Pointer(FP) or Stack Pointer(SP)
+ * @ra_offset: Offset for the Return Address from CFA.
+ * @fp_offset: Offset for the Frame Pointer (FP) from CFA.
+ * @use_fp: Use FP to get next CFA or not
+ */
+struct sframe_ip_entry {
+ int32_t cfa_offset;
+ int32_t ra_offset;
+ int32_t fp_offset;
+ bool use_fp;
+};
+
+/**
+ * struct sframe_table - sframe struct of a table
+ * @sfhdr_p: Pointer to sframe header
+ * @fde_p: Pointer to the first of sframe frame description entry(FDE).
+ * @fre_p: Pointer to the first of sframe frame row entry(FRE).
+ */
+struct sframe_table {
+ struct sframe_header *sfhdr_p;
+ struct sframe_fde *fde_p;
+ char *fre_p;
+};
+
+#ifdef CONFIG_SFRAME_UNWINDER
+void init_sframe_table(void);
+int sframe_find_pc(unsigned long pc, struct sframe_ip_entry *entry);
+#else
+static inline void init_sframe_table(void) {}
+static inline int sframe_find_pc(unsigned long pc, struct sframe_ip_entry *entry)
+{
+ return -EINVAL;
+}
+#endif
+
+#endif /* _LINUX_SFRAME_LOOKUP_H */
diff --git a/kernel/Makefile b/kernel/Makefile
index 87866b037fbe..705c9277e5cd 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -131,6 +131,7 @@ obj-$(CONFIG_WATCH_QUEUE) += watch_queue.o
obj-$(CONFIG_RESOURCE_KUNIT_TEST) += resource_kunit.o
obj-$(CONFIG_SYSCTL_KUNIT_TEST) += sysctl-test.o
+obj-$(CONFIG_SFRAME_UNWINDER) += sframe_lookup.o
CFLAGS_stackleak.o += $(DISABLE_STACKLEAK_PLUGIN)
obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak.o
diff --git a/kernel/sframe_lookup.c b/kernel/sframe_lookup.c
new file mode 100644
index 000000000000..846f1da95d89
--- /dev/null
+++ b/kernel/sframe_lookup.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/module.h>
+#include <linux/sort.h>
+#include <linux/sframe_lookup.h>
+#include <linux/kallsyms.h>
+#include "sframe.h"
+
+#define pr_fmt(fmt) "sframe: " fmt
+
+extern char __start_sframe_header[];
+extern char __stop_sframe_header[];
+
+static bool sframe_init __ro_after_init;
+static struct sframe_table sftbl;
+
+#define SFRAME_READ_TYPE(in, out, type) \
+({ \
+ type __tmp; \
+ memcpy(&__tmp, in, sizeof(__tmp)); \
+ in += sizeof(__tmp); \
+ out = __tmp; \
+})
+
+#define SFRAME_READ_ROW_ADDR(in_addr, out_addr, type) \
+({ \
+ switch (type) { \
+ case SFRAME_FRE_TYPE_ADDR1: \
+ SFRAME_READ_TYPE(in_addr, out_addr, u8); \
+ break; \
+ case SFRAME_FRE_TYPE_ADDR2: \
+ SFRAME_READ_TYPE(in_addr, out_addr, u16); \
+ break; \
+ case SFRAME_FRE_TYPE_ADDR4: \
+ SFRAME_READ_TYPE(in_addr, out_addr, u32); \
+ break; \
+ default: \
+ break; \
+ } \
+})
+
+#define SFRAME_READ_ROW_OFFSETS(in_addr, out_addr, size) \
+({ \
+ switch (size) { \
+ case 1: \
+ SFRAME_READ_TYPE(in_addr, out_addr, s8); \
+ break; \
+ case 2: \
+ SFRAME_READ_TYPE(in_addr, out_addr, s16); \
+ break; \
+ case 4: \
+ SFRAME_READ_TYPE(in_addr, out_addr, s32); \
+ break; \
+ default: \
+ break; \
+ } \
+})
+
+static struct sframe_fde *find_fde(const struct sframe_table *tbl, unsigned long pc)
+{
+ int l, r, m, f;
+ int32_t ip;
+ struct sframe_fde *fdep;
+
+ if (!tbl || !tbl->sfhdr_p || !tbl->fde_p)
+ return NULL;
+
+ ip = (pc - (unsigned long)tbl->sfhdr_p);
+
+ /* Do a binary range search to find the rightmost FDE start_addr < ip */
+ l = m = f = 0;
+ r = tbl->sfhdr_p->num_fdes;
+ while (l < r) {
+ m = l + ((r - l) / 2);
+ fdep = tbl->fde_p + m;
+ if (fdep->start_addr > ip)
+ r = m;
+ else
+ l = m + 1;
+ }
+ /* use l - 1 because l will be the first item fdep->start_addr > ip */
+ f = l - 1;
+ if (f >= tbl->sfhdr_p->num_fdes || f < 0)
+ return NULL;
+ fdep = tbl->fde_p + f;
+ if (ip < fdep->start_addr || ip >= fdep->start_addr + fdep->size)
+ return NULL;
+
+ return fdep;
+}
+
+static int find_fre(const struct sframe_table *tbl, unsigned long pc,
+ const struct sframe_fde *fdep, struct sframe_ip_entry *entry)
+{
+ int i, offset_size, offset_count;
+ char *fres, *offsets_loc;
+ int32_t ip_off;
+ uint32_t next_row_ip_off;
+ uint8_t fre_info, fde_type = SFRAME_FUNC_FDE_TYPE(fdep->info),
+ fre_type = SFRAME_FUNC_FRE_TYPE(fdep->info);
+
+ fres = tbl->fre_p + fdep->fres_off;
+
+ /* Whether PCs in the FREs should be treated as masks or not */
+ if (fde_type == SFRAME_FDE_TYPE_PCMASK)
+ ip_off = pc % fdep->rep_size;
+ else
+ ip_off = (int32_t)(pc - (unsigned long)tbl->sfhdr_p) - fdep->start_addr;
+
+ if (ip_off < 0 || ip_off >= fdep->size)
+ return -EINVAL;
+
+ /*
+ * FRE structure starts by address of the entry with variants length. Use
+ * two pointers to track current head(fres) and the address of last
+ * offset(offsets_loc)
+ */
+ for (i = 0; i < fdep->fres_num; i++) {
+ SFRAME_READ_ROW_ADDR(fres, next_row_ip_off, fre_type);
+ if (ip_off < next_row_ip_off)
+ break;
+ SFRAME_READ_TYPE(fres, fre_info, u8);
+ offsets_loc = fres;
+ /*
+ * jump to the start of next fre
+ * fres += fre_offets_cnt*offset_size
+ */
+ fres += SFRAME_FRE_OFFSET_COUNT(fre_info) << SFRAME_FRE_OFFSET_SIZE(fre_info);
+ }
+
+ offset_size = 1 << SFRAME_FRE_OFFSET_SIZE(fre_info);
+ offset_count = SFRAME_FRE_OFFSET_COUNT(fre_info);
+
+ if (offset_count > 0) {
+ SFRAME_READ_ROW_OFFSETS(offsets_loc, entry->cfa_offset, offset_size);
+ offset_count--;
+ }
+ if (offset_count > 0 && !entry->ra_offset) {
+ SFRAME_READ_ROW_OFFSETS(offsets_loc, entry->ra_offset, offset_size);
+ offset_count--;
+ }
+ if (offset_count > 0 && !entry->fp_offset) {
+ SFRAME_READ_ROW_OFFSETS(offsets_loc, entry->fp_offset, offset_size);
+ offset_count--;
+ }
+ if (offset_count)
+ return -EINVAL;
+
+ entry->use_fp = SFRAME_FRE_CFA_BASE_REG_ID(fre_info) == SFRAME_BASE_REG_FP;
+
+ return 0;
+}
+
+int sframe_find_pc(unsigned long pc, struct sframe_ip_entry *entry)
+{
+ struct sframe_fde *fdep;
+ struct sframe_table *sftbl_p = &sftbl;
+ int err;
+
+ if (!sframe_init)
+ return -EINVAL;
+
+ memset(entry, 0, sizeof(*entry));
+ entry->ra_offset = sftbl_p->sfhdr_p->cfa_fixed_ra_offset;
+ entry->fp_offset = sftbl_p->sfhdr_p->cfa_fixed_fp_offset;
+
+ fdep = find_fde(sftbl_p, pc);
+ if (!fdep)
+ return -EINVAL;
+ err = find_fre(sftbl_p, pc, fdep, entry);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+void __init init_sframe_table(void)
+{
+ size_t sframe_size = (void *)__stop_sframe_header - (void *)__start_sframe_header;
+ void *sframe_buf = __start_sframe_header;
+
+ if (sframe_size <= 0)
+ return;
+ sftbl.sfhdr_p = sframe_buf;
+ if (!sftbl.sfhdr_p || sftbl.sfhdr_p->preamble.magic != SFRAME_MAGIC ||
+ sftbl.sfhdr_p->preamble.version != SFRAME_VERSION_2 ||
+ !(sftbl.sfhdr_p->preamble.flags & SFRAME_F_FDE_SORTED)) {
+ pr_warn("WARNING: Unable to read sframe header. Disabling unwinder.\n");
+ return;
+ }
+
+ sftbl.fde_p = (struct sframe_fde *)(__start_sframe_header + SFRAME_HDR_SIZE(*sftbl.sfhdr_p)
+ + sftbl.sfhdr_p->fdes_off);
+ sftbl.fre_p = __start_sframe_header + SFRAME_HDR_SIZE(*sftbl.sfhdr_p)
+ + sftbl.sfhdr_p->fres_off;
+ sframe_init = true;
+}
--
2.48.1.262.g85cc9f2d1e-goog
next prev parent reply other threads:[~2025-01-27 21:33 UTC|newest]
Thread overview: 92+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-01-27 21:33 [PATCH 0/8] unwind, arm64: add sframe unwinder for kernel Weinan Liu
2025-01-27 21:33 ` [PATCH 1/8] unwind: build kernel with sframe info Weinan Liu
2025-01-30 9:45 ` Prasanna Kumar T S M
2025-02-05 0:22 ` Indu Bhagat
2025-02-07 18:01 ` Josh Poimboeuf
2025-01-27 21:33 ` [PATCH 2/8] arm64: entry: add unwind info for various kernel entries Weinan Liu
2025-01-27 21:33 ` [PATCH 3/8] unwind: add sframe v2 header Weinan Liu
2025-01-30 9:53 ` Prasanna Kumar T S M
2025-02-07 18:05 ` Josh Poimboeuf
2025-01-27 21:33 ` Weinan Liu [this message]
2025-01-30 10:22 ` [PATCH 4/8] unwind: Implement generic sframe unwinder library Prasanna Kumar T S M
2025-01-30 10:29 ` Prasanna Kumar T S M
2025-02-02 6:27 ` Weinan Liu
2025-02-02 6:37 ` Weinan Liu
2025-01-27 21:33 ` [PATCH 5/8] unwind: arm64: Add sframe unwinder on arm64 Weinan Liu
2025-01-30 10:34 ` Prasanna Kumar T S M
2025-01-27 21:33 ` [PATCH 6/8] unwind: arm64: add reliable stacktrace support for arm64 Weinan Liu
2025-01-30 10:36 ` Prasanna Kumar T S M
2025-01-27 21:33 ` [PATCH 7/8] arm64: Define TIF_PATCH_PENDING for livepatch Weinan Liu
2025-01-30 9:54 ` Prasanna Kumar T S M
2025-02-27 12:10 ` Miroslav Benes
2025-01-27 21:33 ` [PATCH 8/8] arm64: Enable livepatch for ARM64 Weinan Liu
2025-01-30 9:55 ` Prasanna Kumar T S M
2025-01-31 16:08 ` Prasanna Kumar T S M
2025-02-03 15:16 ` Steven Rostedt
2025-01-28 15:35 ` [PATCH 0/8] unwind, arm64: add sframe unwinder for kernel Indu Bhagat
2025-01-29 7:23 ` Weinan Liu
2025-01-30 17:59 ` Song Liu
2025-01-30 18:34 ` Song Liu
2025-01-30 19:01 ` Roman Gushchin
2025-01-30 19:18 ` Song Liu
2025-02-04 14:49 ` Puranjay Mohan
2025-02-04 23:52 ` Puranjay Mohan
2025-02-06 15:02 ` Weinan Liu
2025-02-07 12:16 ` Puranjay Mohan
2025-02-07 17:52 ` Josh Poimboeuf
2025-02-10 8:30 ` Weinan Liu
2025-02-25 1:02 ` Weinan Liu
2025-02-25 18:13 ` Josh Poimboeuf
2025-02-25 23:01 ` Weinan Liu
2025-02-25 19:38 ` Indu Bhagat
2025-02-25 23:54 ` Weinan Liu
2025-02-26 0:22 ` Indu Bhagat
2025-02-26 10:23 ` Puranjay Mohan
2025-02-26 17:40 ` Indu Bhagat
2025-02-27 9:38 ` Puranjay Mohan
2025-02-28 6:47 ` Indu Bhagat
2025-03-09 14:43 ` Indu Bhagat
2025-02-12 23:32 ` Song Liu
2025-02-12 23:49 ` Josh Poimboeuf
2025-02-13 2:36 ` Song Liu
2025-02-13 2:45 ` Josh Poimboeuf
2025-02-13 7:25 ` Song Liu
2025-02-13 7:46 ` Puranjay Mohan
2025-02-13 19:40 ` Song Liu
2025-02-14 8:08 ` Josh Poimboeuf
2025-02-14 17:51 ` Song Liu
2025-02-14 19:34 ` Josh Poimboeuf
2025-02-14 22:04 ` Song Liu
2025-02-14 22:33 ` Josh Poimboeuf
2025-02-14 23:23 ` Josh Poimboeuf
2025-02-18 4:38 ` Song Liu
2025-02-18 6:37 ` Josh Poimboeuf
2025-02-18 18:20 ` Song Liu
2025-02-18 18:40 ` Josh Poimboeuf
2025-02-19 17:44 ` Song Liu
2025-02-20 4:50 ` Song Liu
2025-02-20 18:22 ` Josh Poimboeuf
[not found] ` <CAPhsuW53DK2vFH-BZeUYN-eythX3NQEbcxrYf6jvBDtDmctRgw@mail.gmail.com>
2025-02-25 0:13 ` Song Liu
2025-02-13 23:22 ` Indu Bhagat
2025-02-13 23:47 ` Song Liu
2025-02-14 7:57 ` Puranjay Mohan
2025-02-14 17:39 ` Indu Bhagat
2025-02-14 18:41 ` Indu Bhagat
2025-02-14 18:58 ` Puranjay Mohan
2025-02-14 19:38 ` Josh Poimboeuf
2025-02-14 19:42 ` Josh Poimboeuf
2025-02-13 0:09 ` Indu Bhagat
2025-02-13 2:40 ` Song Liu
2025-02-13 2:52 ` Josh Poimboeuf
2025-02-13 7:26 ` Puranjay Mohan
2025-02-13 7:37 ` Song Liu
2025-02-13 7:53 ` Puranjay Mohan
2025-02-13 19:42 ` Song Liu
2025-02-13 8:37 ` Puranjay Mohan
2025-02-13 20:46 ` Song Liu
2025-02-13 22:21 ` Puranjay Mohan
2025-02-13 23:34 ` Song Liu
2025-02-14 1:58 ` Song Liu
2025-02-14 8:56 ` Puranjay Mohan
2025-02-14 18:10 ` Song Liu
2025-02-14 18:24 ` Josh Poimboeuf
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250127213310.2496133-5-wnliu@google.com \
--to=wnliu@google.com \
--cc=indu.bhagat@oracle.com \
--cc=irogers@google.com \
--cc=joe.lawrence@redhat.com \
--cc=jpoimboe@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-toolchains@vger.kernel.org \
--cc=live-patching@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=peterz@infradead.org \
--cc=roman.gushchin@linux.dev \
--cc=rostedt@goodmis.org \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®