Received: by 2002:a05:6358:d09b:b0:dc:cd0c:909e with SMTP id jc27csp438566rwb; Wed, 14 Dec 2022 20:10:51 -0800 (PST) X-Google-Smtp-Source: AMrXdXtd+cbZl9by81I04gIV5M5NzgxKjYzbgq6/xhngI69alyMI9quVoNo6eRqBcqCMefWcUtz0 X-Received: by 2002:a05:6402:538a:b0:468:ccfb:bed4 with SMTP id ew10-20020a056402538a00b00468ccfbbed4mr4107824edb.28.1671077450940; Wed, 14 Dec 2022 20:10:50 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1671077450; cv=none; d=google.com; s=arc-20160816; b=Xp/YbpPo7DTMJdJtlPNaCoH2CkYo0TLg6XdPW+uRKVkOGUUtCJzv32NIYwMvBWTHLy MryMs7R6lleRBKppINgXANm156IwiXSSVhWO1LFsCgIflK/JERwqwIF6Fn+MdNSnQyGX GT4jr480oAubSMeKNCAP6mCBxILfXlthjeypw9ri+RWxmmmF4bE6xyvi76fn5oyERyUO QdoH8dUBfPQKNDs8TL01Z4z0g3Fxm1QRLNG85VhrRw8iR983klckcNHrjVXGmLTkalmV jkOMmW4dbgZV/h4KqOVpKc3gRfYyWRZydSht7XzoKVMTzbuJe7Tj1PPbGSm/IiMFc7GJ 76mA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=PRC7NhJjVUUz9gufz1KfcHgDRHeQ7Cm2Jrqx5VZwx4Y=; b=eN99nx7/kNyE+NdKQeWS0otXmf1sOWZVhDdcZbcCremIyIRaWY1/40l5LdfMrM5aN1 yMIokZ4mmrp4JEsputE1ymhmC1EV1z5h3zyReSzaXBJdQaX+KzuRC/MLne8wnTkMtyLV F38ySYP3oIjuGrAWHwIw2izSS92aWbrfOJKUyLxvC8UjKwDDIkjngzILZD290Xj/etSv yiM7I5F4QDkoQq1Zqo+mVPMwtXbn1aYDuRI+0EZieEXb6r154psScPvlyew6Puj9WH44 m6YFgN9iCqOD2w8BTJxmHlsf809aKLZHtwyFvsSNuwFF0RurCI+3Y2xIBp/fcqpDC0Ex ITfg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id g39-20020a056402322700b004696e8f74cbsi13214554eda.537.2022.12.14.20.10.32; Wed, 14 Dec 2022 20:10:50 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229734AbiLOED1 (ORCPT + 69 others); Wed, 14 Dec 2022 23:03:27 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54234 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229681AbiLOECT (ORCPT ); Wed, 14 Dec 2022 23:02:19 -0500 Received: from loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id DCFA82019A for ; Wed, 14 Dec 2022 20:02:17 -0800 (PST) Received: from loongson.cn (unknown [111.9.175.10]) by gateway (Coremail) with SMTP id _____8DxuupInJpj8rkFAA--.13377S3; Thu, 15 Dec 2022 12:02:16 +0800 (CST) Received: from localhost.localdomain (unknown [111.9.175.10]) by localhost.localdomain (Coremail) with SMTP id AQAAf8BxtOVDnJpjVBkAAA--.887S6; Thu, 15 Dec 2022 12:02:16 +0800 (CST) From: Jinyang He To: Huacai Chen , WANG Xuerui , Qing Zhang Cc: loongarch@lists.linux.dev, linux-kernel@vger.kernel.org, Steven Rostedt , Masami Hiramatsu , Mark Rutland Subject: [PATCH 4/6] LoongArch: Strip guess_unwinder out from prologue_unwinder Date: Thu, 15 Dec 2022 12:01:39 +0800 Message-Id: <20221215040141.18610-5-hejinyang@loongson.cn> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20221215040141.18610-1-hejinyang@loongson.cn> References: <20221215040141.18610-1-hejinyang@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-CM-TRANSID: AQAAf8BxtOVDnJpjVBkAAA--.887S6 X-CM-SenderInfo: pkhmx0p1dqwqxorr0wxvrqhubq/ X-Coremail-Antispam: 1Uk129KBjvAXoW3KryfCrW3uF4DWrW5XF45GFg_yoW8Jw1fCo WftF4agr48X3y5t3yjyryUtFyYgr4jka1UAw43trn8Wr42y343ur4jqasxXFyIqw1rKrWU Gr42gF4rXan7Arn3n29KB7ZKAUJUUUU7529EdanIXcx71UUUUU7KY7ZEXasCq-sGcSsGvf J3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU0xBIdaVrnRJU UUBIb4IE77IF4wAFF20E14v26r1j6r4UM7CY07I20VC2zVCF04k26cxKx2IYs7xG6rWj6s 0DM7CIcVAFz4kK6r1Y6r17M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48ve4kI8wA2z4x0 Y4vE2Ix0cI8IcVAFwI0_Xr0_Ar1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI0_Gr0_Cr1l84 ACjcxK6I8E87Iv67AKxVW8Jr0_Cr1UM28EF7xvwVC2z280aVCY1x0267AKxVW8Jr0_Cr1U M2kKe7AKxVWUXVWUAwAS0I0E0xvYzxvE52x082IY62kv0487Mc804VCY07AIYIkI8VC2zV CFFI0UMc02F40EFcxC0VAKzVAqx4xG6I80ewAv7VC0I7IYx2IY67AKxVWUtVWrXwAv7VC2 z280aVAFwI0_Gr0_Cr1lOx8S6xCaFVCjc4AY6r1j6r4UM4x0Y48IcxkI7VAKI48JMxkF7I 0En4kS14v26r126r1DMxAIw28IcxkI7VAKI48JMxC20s026xCaFVCjc4AY6r1j6r4UMxCI bckI1I0E14v26r1Y6r17MI8I3I0E5I8CrVAFwI0_Jr0_Jr4lx2IqxVCjr7xvwVAFwI0_Jr I_JrWlx4CE17CEb7AF67AKxVWUtVW8ZwCIc40Y0x0EwIxGrwCI42IY6xIIjxv20xvE14v2 6ryj6F1UMIIF0xvE2Ix0cI8IcVCY1x0267AKxVW8JVWxJwCI42IY6xAIw20EY4v20xvaj4 0_Jr0_JF4lIxAIcVC2z280aVAFwI0_Gr0_Cr1lIxAIcVC2z280aVCY1x0267AKxVW8JVW8 JrUvcSsGvfC2KfnxnUUI43ZEXa7IU0epB3UUUUU== X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,SPF_HELO_PASS, SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The prolugue unwinder rely on symbol info. When PC is not in kernel text address, it cannot find relative symbol info and it will be broken. The guess unwinder will be used in this case. And the guess unwinder codes in prolugue unwinder is redundant. Strip it out and set the unwinder info in unwind_state. Signed-off-by: Jinyang He --- arch/loongarch/include/asm/unwind.h | 22 ++++ arch/loongarch/kernel/Makefile | 3 +- arch/loongarch/kernel/unwind.c | 52 +++++++++ arch/loongarch/kernel/unwind_guess.c | 41 ++----- arch/loongarch/kernel/unwind_prologue.c | 135 +++++++++--------------- 5 files changed, 135 insertions(+), 118 deletions(-) create mode 100644 arch/loongarch/kernel/unwind.c diff --git a/arch/loongarch/include/asm/unwind.h b/arch/loongarch/include/asm/unwind.h index 6ece48f0ff77..a16aff1d086a 100644 --- a/arch/loongarch/include/asm/unwind.h +++ b/arch/loongarch/include/asm/unwind.h @@ -18,6 +18,8 @@ enum unwinder_type { UNWINDER_PROLOGUE, }; +struct unwinder_ops; + struct unwind_state { char type; /* UNWINDER_XXX */ struct stack_info stack_info; @@ -25,8 +27,22 @@ struct unwind_state { bool first, error, is_ftrace; int graph_idx; unsigned long sp, pc, ra; + const struct unwinder_ops *ops; +}; + +struct unwinder_ops { + void (*unwind_start)(struct unwind_state *state, + struct task_struct *task, struct pt_regs *regs); + bool (*unwind_next_frame)(struct unwind_state *state); + unsigned long (*unwind_get_return_address)(struct unwind_state *state); }; +extern const struct unwinder_ops *default_unwinder; +extern const struct unwinder_ops unwinder_guess; +#ifdef CONFIG_UNWINDER_PROLOGUE +extern const struct unwinder_ops unwinder_prologue; +#endif + void unwind_start(struct unwind_state *state, struct task_struct *task, struct pt_regs *regs); bool unwind_next_frame(struct unwind_state *state); @@ -49,4 +65,10 @@ static inline unsigned long unwind_graph_addr(struct unwind_state *state, return ftrace_graph_ret_addr(state->task, &state->graph_idx, pc, (unsigned long *)(cfa - GRAPH_FAKE_OFFSET)); } + +static inline void unwind_register_unwinder(struct unwind_state *state, + const struct unwinder_ops *unwinder) +{ + state->ops = unwinder; +} #endif /* _ASM_UNWIND_H */ diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile index 7ca65195f7f8..cb6029ea3ea9 100644 --- a/arch/loongarch/kernel/Makefile +++ b/arch/loongarch/kernel/Makefile @@ -8,7 +8,7 @@ extra-y := vmlinux.lds obj-y += head.o cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \ traps.o irq.o idle.o process.o dma.o mem.o io.o reset.o switch.o \ elf.o syscall.o signal.o time.o topology.o inst.o ptrace.o vdso.o \ - alternative.o unaligned.o + alternative.o unaligned.o unwind.o unwind_guess.o obj-$(CONFIG_ACPI) += acpi.o obj-$(CONFIG_EFI) += efi.o @@ -42,7 +42,6 @@ obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o -obj-$(CONFIG_UNWINDER_GUESS) += unwind_guess.o obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_regs.o diff --git a/arch/loongarch/kernel/unwind.c b/arch/loongarch/kernel/unwind.c new file mode 100644 index 000000000000..568c6fe707d1 --- /dev/null +++ b/arch/loongarch/kernel/unwind.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Loongson Technology Corporation Limited + */ +#include + +#if defined(CONFIG_UNWINDER_GUESS) +const struct unwinder_ops *default_unwinder = &unwinder_guess; +#elif defined(CONFIG_UNWINDER_PROLOGUE) +const struct unwinder_ops *default_unwinder = &unwinder_prologue; +#endif + +unsigned long unwind_get_return_address(struct unwind_state *state) +{ + if (!state->ops || unwind_done(state)) + return 0; + return state->ops->unwind_get_return_address(state); +} +EXPORT_SYMBOL_GPL(unwind_get_return_address); + +void unwind_start(struct unwind_state *state, struct task_struct *task, + struct pt_regs *regs) +{ + memset(state, 0, sizeof(*state)); + unwind_register_unwinder(state, default_unwinder); + if (regs) { + state->sp = regs->regs[3]; + state->pc = regs->csr_era; + state->ra = regs->regs[1]; + } else if (task == current) { + state->sp = (unsigned long)__builtin_frame_address(0); + state->pc = (unsigned long)__builtin_return_address(0); + state->ra = 0; + } else { + state->sp = thread_saved_fp(task); + state->pc = thread_saved_ra(task); + state->ra = 0; + } + state->task = task; + get_stack_info(state->sp, state->task, &state->stack_info); + state->pc = unwind_graph_addr(state, state->pc, state->sp); + state->ops->unwind_start(state, task, regs); +} +EXPORT_SYMBOL_GPL(unwind_start); + +bool unwind_next_frame(struct unwind_state *state) +{ + if (!state->ops || unwind_done(state)) + return false; + return state->ops->unwind_next_frame(state); +} +EXPORT_SYMBOL_GPL(unwind_next_frame); diff --git a/arch/loongarch/kernel/unwind_guess.c b/arch/loongarch/kernel/unwind_guess.c index 8ce32c37c587..b7ca2b88ac63 100644 --- a/arch/loongarch/kernel/unwind_guess.c +++ b/arch/loongarch/kernel/unwind_guess.c @@ -7,51 +7,23 @@ #include -unsigned long unwind_get_return_address(struct unwind_state *state) +static unsigned long get_return_address(struct unwind_state *state) { - if (unwind_done(state)) - return 0; return state->pc; } -EXPORT_SYMBOL_GPL(unwind_get_return_address); -void unwind_start(struct unwind_state *state, struct task_struct *task, +static void start(struct unwind_state *state, struct task_struct *task, struct pt_regs *regs) { - memset(state, 0, sizeof(*state)); - - if (regs) { - state->sp = regs->regs[3]; - state->pc = regs->csr_era; - } else if (task == current) { - state->sp = (unsigned long)__builtin_frame_address(0); - state->pc = (unsigned long)__builtin_return_address(0); - } else { - state->sp = thread_saved_fp(task); - state->pc = thread_saved_ra(task); - } - - state->task = task; - state->first = true; - state->pc = unwind_graph_addr(state, state->pc, state->sp); - get_stack_info(state->sp, state->task, &state->stack_info); - if (!unwind_done(state) && !__kernel_text_address(state->pc)) unwind_next_frame(state); } -EXPORT_SYMBOL_GPL(unwind_start); -bool unwind_next_frame(struct unwind_state *state) +static bool next_frame(struct unwind_state *state) { struct stack_info *info = &state->stack_info; unsigned long addr; - if (unwind_done(state)) - return false; - - if (state->first) - state->first = false; - do { for (state->sp += sizeof(unsigned long); state->sp < info->end; @@ -68,4 +40,9 @@ bool unwind_next_frame(struct unwind_state *state) return false; } -EXPORT_SYMBOL_GPL(unwind_next_frame); + +const struct unwinder_ops unwinder_guess = { + .unwind_start = start, + .unwind_next_frame = next_frame, + .unwind_get_return_address = get_return_address, +}; diff --git a/arch/loongarch/kernel/unwind_prologue.c b/arch/loongarch/kernel/unwind_prologue.c index d464c533c64f..9677e13c4b4c 100644 --- a/arch/loongarch/kernel/unwind_prologue.c +++ b/arch/loongarch/kernel/unwind_prologue.c @@ -9,6 +9,8 @@ #include #include +static const struct unwinder_ops *guard_unwinder = &unwinder_guess; + static inline void unwind_state_fixup(struct unwind_state *state) { #ifdef CONFIG_DYNAMIC_FTRACE @@ -19,31 +21,19 @@ static inline void unwind_state_fixup(struct unwind_state *state) #endif } -unsigned long unwind_get_return_address(struct unwind_state *state) +static unsigned long get_return_address(struct unwind_state *state) { - if (unwind_done(state)) - return 0; return state->pc; } -EXPORT_SYMBOL_GPL(unwind_get_return_address); - -static bool unwind_by_guess(struct unwind_state *state) -{ - struct stack_info *info = &state->stack_info; - unsigned long addr; - - for (state->sp += sizeof(unsigned long); - state->sp < info->end; - state->sp += sizeof(unsigned long)) { - addr = *(unsigned long *)(state->sp); - state->pc = unwind_graph_addr(state, addr, state->sp + 8); - if (__kernel_text_address(state->pc)) - return true; - } - - return false; -} +/* + * LoongArch function prologue like follows, + * [others instructions not use stack var] + * addi.d sp, sp, -imm + * st.d xx, sp, offset <- save callee saved regs and + * st.d yy, sp, offset save ra if function is nest. + * [others instructions] + */ static bool unwind_by_prologue(struct unwind_state *state) { long frame_ra = -1; @@ -89,6 +79,10 @@ static bool unwind_by_prologue(struct unwind_state *state) ip++; } + /* + * Not find stack alloc action, PC may be in a leaf function. Only the + * first being true is reasonable, otherwise indicate analysis is broken. + */ if (!frame_size) { if (state->first) goto first; @@ -106,6 +100,7 @@ static bool unwind_by_prologue(struct unwind_state *state) ip++; } + /* Not find save $ra action, PC may be in a leaf function, too. */ if (frame_ra < 0) { if (state->first) { state->sp = state->sp + frame_size; @@ -114,96 +109,63 @@ static bool unwind_by_prologue(struct unwind_state *state) return false; } - if (state->first) - state->first = false; - state->pc = *(unsigned long *)(state->sp + frame_ra); state->sp = state->sp + frame_size; goto out; first: - state->first = false; - if (state->pc == state->ra) - return false; - state->pc = state->ra; out: + state->first = false; unwind_state_fixup(state); return !!__kernel_text_address(state->pc); } -void unwind_start(struct unwind_state *state, struct task_struct *task, +static void start(struct unwind_state *state, struct task_struct *task, struct pt_regs *regs) { - memset(state, 0, sizeof(*state)); - state->type = UNWINDER_PROLOGUE; - - if (regs) { - state->sp = regs->regs[3]; - state->pc = regs->csr_era; - state->ra = regs->regs[1]; - if (!__kernel_text_address(state->pc)) - state->type = UNWINDER_GUESS; - } else if (task == current) { - state->sp = (unsigned long)__builtin_frame_address(0); - state->pc = (unsigned long)__builtin_return_address(0); - state->ra = 0; - } else { - state->sp = thread_saved_fp(task); - state->pc = thread_saved_ra(task); - state->ra = 0; - } - - state->task = task; state->first = true; - state->pc = unwind_graph_addr(state, state->pc, state->sp); - get_stack_info(state->sp, state->task, &state->stack_info); - if (!unwind_done(state) && !__kernel_text_address(state->pc)) - unwind_next_frame(state); + /* + * The current PC is not kernel text address, we cannot find its + * relative symbol. Thus, prologue analysis will be broken. Luckly, + * we can use the guard unwinder. + */ + if (!__kernel_text_address(state->pc)) { + unwind_register_unwinder(state, guard_unwinder); + if (!unwind_done(state)) + unwind_next_frame(state); + } } -EXPORT_SYMBOL_GPL(unwind_start); -bool unwind_next_frame(struct unwind_state *state) +static bool next_frame(struct unwind_state *state) { struct stack_info *info = &state->stack_info; struct pt_regs *regs; unsigned long pc; - if (unwind_done(state)) - return false; - do { - switch (state->type) { - case UNWINDER_GUESS: - state->first = false; - if (unwind_by_guess(state)) - return true; - break; - - case UNWINDER_PROLOGUE: - if (unwind_by_prologue(state)) { - state->pc = unwind_graph_addr(state, state->pc, state->sp); - return true; - } + if (unwind_by_prologue(state)) { + state->pc = unwind_graph_addr(state, state->pc, state->sp); + return true; + } - if (info->type == STACK_TYPE_IRQ && - info->end == state->sp) { - regs = (struct pt_regs *)info->next_sp; - pc = regs->csr_era; + if (info->type == STACK_TYPE_IRQ && + info->end == state->sp) { + regs = (struct pt_regs *)info->next_sp; + pc = regs->csr_era; - if (user_mode(regs) || !__kernel_text_address(pc)) - return false; + if (user_mode(regs) || !__kernel_text_address(pc)) + return false; - state->first = true; - state->ra = regs->regs[1]; - state->sp = regs->regs[3]; - state->pc = pc; - get_stack_info(state->sp, state->task, info); + state->first = true; + state->ra = regs->regs[1]; + state->sp = regs->regs[3]; + state->pc = pc; + get_stack_info(state->sp, state->task, info); - return true; - } + return true; } state->sp = info->next_sp; @@ -212,4 +174,9 @@ bool unwind_next_frame(struct unwind_state *state) return false; } -EXPORT_SYMBOL_GPL(unwind_next_frame); + +const struct unwinder_ops unwinder_prologue = { + .unwind_start = start, + .unwind_next_frame = next_frame, + .unwind_get_return_address = get_return_address, +}; -- 2.34.3