From: Prasanna Kumar T S M <ptsm@linux.microsoft.com>
To: Weinan Liu <wnliu@google.com>,
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
Subject: Re: [PATCH 4/8] unwind: Implement generic sframe unwinder library
Date: Thu, 30 Jan 2025 15:52:52 +0530 [thread overview]
Message-ID: <c034b597-e128-4356-bcc9-79fb5e39a844@linux.microsoft.com> (raw)
In-Reply-To: <20250127213310.2496133-5-wnliu@google.com>
On 28-01-2025 03:03, Weinan Liu wrote:
> 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;
The ra_offset is not present for x86_64 in SFrame FRE as per the spec. I
am wondering whether this struct should change based on the architecture
or just set ra_offset calculated from cfa_offset for x86_64.
> + 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;
> +}
Other than the minor suggestion, the code looks good to me.
Reviewed-by: Prasanna Kumar T S M <ptsm@linux.microsoft.com>.
next prev parent reply other threads:[~2025-01-30 10:25 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 ` [PATCH 4/8] unwind: Implement generic sframe unwinder library Weinan Liu
2025-01-30 10:22 ` Prasanna Kumar T S M [this message]
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=c034b597-e128-4356-bcc9-79fb5e39a844@linux.microsoft.com \
--to=ptsm@linux.microsoft.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 \
--cc=wnliu@google.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.