From: Jinyang He <hejinyang@loongson.cn>
To: Xi Ruoyao <xry111@xry111.site>,
Nathan Chancellor <nathan@kernel.org>,
Peter Zijlstra <peterz@infradead.org>
Cc: Huacai Chen <chenhuacai@kernel.org>,
WANG Xuerui <kernel@xen0n.name>,
Tiezhu Yang <yangtiezhu@loongson.cn>,
Nick Desaulniers <ndesaulniers@google.com>,
Bill Wendling <morbo@google.com>,
Justin Stitt <justinstitt@google.com>,
Youling Tang <tangyouling@kylinos.cn>,
loongarch@lists.linux.dev, linux-kernel@vger.kernel.org,
llvm@lists.linux.dev, mengqinggang@loongson.cn,
cailulu@loongson.cn, wanglei@loongson.cn, luweining@loongson.cn,
Yujie Liu <yujie.liu@intel.com>,
Heng Qi <hengqi@linux.alibaba.com>, Tejun Heo <tj@kernel.org>
Subject: Re: [PATCH] loongarch: Only select HAVE_OBJTOOL and allow ORC unwinder if the inline assembler supports R_LARCH_{32,64}_PCREL
Date: Wed, 5 Jun 2024 23:47:44 +0800 [thread overview]
Message-ID: <82b7e6ea-c2cb-6364-ebe9-bff928028408@loongson.cn> (raw)
In-Reply-To: <ada035690e446909c3cdbf9a43a92def96020615.camel@xry111.site>
[-- Attachment #1: Type: text/plain, Size: 4618 bytes --]
On 2024-06-05 23:13, Xi Ruoyao wrote:
> On Wed, 2024-06-05 at 21:18 +0800, Xi Ruoyao wrote:
>> On Wed, 2024-06-05 at 18:57 +0800, Xi Ruoyao wrote:
>>> On Tue, 2024-06-04 at 23:25 -0700, Nathan Chancellor wrote:
>>>> On Wed, Jun 05, 2024 at 01:54:24PM +0800, Xi Ruoyao wrote:
>>>>> On Tue, 2024-06-04 at 22:43 -0700, Nathan Chancellor wrote:
>>>>>> For what it's worth, I have noticed some warnings with clang that I
>>>>>> don't see with GCC but I only filed an issue on our GitHub and never
>>>>>> followed up on the mailing list, so sorry about that.
>>>>>>
>>>>>> https://github.com/ClangBuiltLinux/linux/issues/2024
>>>>>>
>>>>>> Might be tangential to this patch though but I felt it was worth
>>>>>> mentioning.
>>>>> The warnings in GCC build is definitely the issue handled by this patch.
>>>>> But the warnings in Clang build should be a different issue. Can you
>>>>> attach the kernel/events/core.o file from the Clang build for analysis?
>>>>> I guess we need to disable more optimization...
>>>> Sure thing. Let me know if there are any issues with the attachment.
>>> Thanks! I've simplified it and now even...
>>>
>>> .global test
>>> .type test,@function
>>> test:
>>>
>>> addi.d $sp,$sp,-448
>>> st.d $ra,$sp,440
>>> st.d $fp,$sp,432
>>> addi.d $fp,$sp,448
>>>
>>> # do something
>>>
>>> addi.d $sp,$fp,-448
>>> ld.d $fp,$sp,432
>>> ld.d $ra,$sp,440
>>> addi.d $sp,$sp,448
>>> ret
>>>
>>> .size test,.-test
>>>
>>> is enough to trigger a objtool warning:
>>>
>>> /home/xry111/t1.o: warning: objtool: test+0x20: return with modified stack frame
>>>
>>> And to me this warning is bogus?
>> Minimal C reproducer:
>>
>> struct x { _Alignas(64) char buf[128]; };
>>
>> void f(struct x *p);
>> void g()
>> {
>> struct x x = { .buf = "1145141919810" };
>> f(&x);
>> }
>>
>> Then objtool is unhappy to the object file produced with "clang -c -O2"
>> from this translation unit:
>>
>> /home/xry111/t2.o: warning: objtool: g+0x50: return with modified stack frame
>>
>> It seems CFI_BP has a very specific semantic in objtool and Clang does
>> not operates $fp in the expected way. I'm not sure about my conclusion
>> though. Maybe Peter can explain it better.
> Another example: some simple rust code:
>
> extern { fn f(x: &i64) -> i64; }
>
> #[no_mangle]
> fn g() -> i64 {
> let x = 114514;
> unsafe {f(&x)}
> }
>
> It's just lucky GCC doesn't use $fp as the frame pointer unless there's
> some stupid code (VLA etc) thus the issue was not detected.
>
>
I think this because we did not add arch special handle in
update_cfi_state().
In our 419 repo this func has been renamed arch_update_insn_state (, now it
should be arch_update_cfi_state) and give some actions to deal with the
frame pointer case. If we support it we may deal with some case but for
clang...
.global test
.type test,@function
test:
addi.d $sp,$sp,-448
st.d $ra,$sp,440
st.d $fp,$sp,432
addi.d $fp,$sp,448
# do something <- not change $sp
# addi.d $sp,$fp,-448 <- not restore sp from cfa(fp)
ld.d $fp,$sp,432
ld.d $ra,$sp,440
addi.d $sp,$sp,448
ret
.size test,.-test
This will cause warning, too. (Gcc should be ok, I don't test.)
source.c: (clang 19, based 0c1c0d53931, -g)
void *foo() { return __builtin_frame_address(0); }
asm.s:
.cfi_sections .debug_frame
.cfi_startproc
# %bb.0:
addi.d $sp, $sp, -16
.cfi_def_cfa_offset 16
st.d $ra, $sp, 8 # 8-byte Folded Spill
st.d $fp, $sp, 0 # 8-byte Folded Spill
.cfi_offset 1, -8
.cfi_offset 22, -16
addi.d $fp, $sp, 16
.cfi_def_cfa 22, 0 <- define cfa is $r22
.Ltmp0:
.loc 0 1 22 prologue_end # hello.c:1:22
move $a0, $fp
ld.d $fp, $sp, 0 # 8-byte Folded Reload
<- restore regs from non-cfa ?
ld.d $ra, $sp, 8 # 8-byte Folded Reload
<- restore regs from non-cfa ?
.loc 0 1 15 epilogue_begin is_stmt 0 # hello.c:1:15
addi.d $sp, $sp, 16
<- restore prev-sp from non-cfa ?
ret
Maybe Clang have anything wrong?
Attach: tmp fix by support arch_update_insn_state.
Thanks,
Jinyang
[-- Attachment #2: 0001-tmp-fix.patch --]
[-- Type: text/x-patch, Size: 30644 bytes --]
From 98d31f806c018a0c5f5fe72e13bf5d107d94d93b Mon Sep 17 00:00:00 2001
From: Jinyang He <hejinyang@loongson.cn>
Date: Wed, 5 Jun 2024 21:26:36 +0800
Subject: [PATCH] tmp fix
---
tools/objtool/arch/loongarch/decode.c | 111 ++++++
tools/objtool/arch/x86/decode.c | 507 ++++++++++++++++++++++++++
tools/objtool/check.c | 505 +------------------------
tools/objtool/include/objtool/arch.h | 4 +
4 files changed, 623 insertions(+), 504 deletions(-)
diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c
index aee479d2191c..ce4f1c140308 100644
--- a/tools/objtool/arch/loongarch/decode.c
+++ b/tools/objtool/arch/loongarch/decode.c
@@ -354,3 +354,114 @@ void arch_initial_func_cfi_state(struct cfi_init_state *state)
state->cfa.base = CFI_SP;
state->cfa.offset = 0;
}
+
+static int update_cfi_state_regs(struct instruction *insn,
+ struct cfi_state *cfi,
+ struct stack_op *op)
+{
+ struct cfi_reg *cfa = &cfi->cfa;
+
+ if (cfa->base != CFI_SP && cfa->base != CFI_SP_INDIRECT)
+ return 0;
+
+ /* addi.d sp, sp, imm */
+ if (op->dest.type == OP_DEST_REG && op->src.type == OP_SRC_ADD &&
+ op->dest.reg == CFI_SP && op->src.reg == CFI_SP)
+ cfa->offset -= op->src.offset;
+
+ return 0;
+}
+
+static void save_reg(struct cfi_state *cfi, unsigned char reg, int base,
+ int offset)
+{
+ if (arch_callee_saved_reg(reg) &&
+ cfi->regs[reg].base == CFI_UNDEFINED) {
+ cfi->regs[reg].base = base;
+ cfi->regs[reg].offset = offset;
+ }
+}
+
+static void restore_reg(struct cfi_state *cfi, unsigned char reg)
+{
+ cfi->regs[reg].base = CFI_UNDEFINED;
+ cfi->regs[reg].offset = 0;
+}
+
+int arch_update_cfi_state(struct instruction *insn,
+ struct instruction *next_insn,
+ struct cfi_state *cfi, struct stack_op *op)
+{
+ struct cfi_reg *cfa = &cfi->cfa;
+ struct cfi_reg *regs = cfi->regs;
+
+ /* ignore UNWIND_HINT_UNDEFINED regions */
+ if (cfi->force_undefined)
+ return 0;
+
+ if (cfa->base == CFI_UNDEFINED) {
+ if (insn_func(insn)) {
+ WARN_INSN(insn, "undefined stack state");
+ return -1;
+ }
+ return 0;
+ }
+
+ if (cfi->type == UNWIND_HINT_TYPE_REGS)
+ return update_cfi_state_regs(insn, cfi, op);
+
+ switch (op->dest.type) {
+ case OP_DEST_REG:
+ switch (op->src.type) {
+ case OP_SRC_ADD:
+ if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) {
+ /* addi.d sp, sp, imm */
+ cfi->stack_size -= op->src.offset;
+ if (cfa->base == CFI_SP)
+ /* addi.d sp, sp, imm */
+ cfi->stack_size -= op->src.offset;
+ if (cfa->base == CFI_SP)
+ cfa->offset -= op->src.offset;
+ } else if (op->dest.reg == CFI_FP && op->src.reg == CFI_SP) {
+ /* addi.d fp, sp, imm */
+ if (cfa->base == CFI_SP && cfa->offset == op->src.offset) {
+ cfa->base = CFI_FP;
+ cfa->offset = 0;
+ }
+ } else if (op->dest.reg == CFI_SP && op->src.reg == CFI_FP) {
+ /* addi.d sp, fp, imm */
+ if (cfa->base == CFI_FP && cfa->offset == 0) {
+ cfa->base = CFI_SP;
+ cfa->offset = -op->src.offset;
+ }
+ }
+ break;
+ case OP_SRC_REG_INDIRECT:
+ /* ld.d _reg, sp, imm */
+ if (op->src.reg == CFI_SP &&
+ op->src.offset == (regs[op->dest.reg].offset + cfi->stack_size)) {
+ restore_reg(cfi, op->dest.reg);
+ /* Gcc may not restore sp, we adjust it directly. */
+ if (cfa->base == CFI_FP && cfa->offset == 0) {
+ cfa->base = CFI_SP;
+ cfa->offset = cfi->stack_size;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case OP_DEST_REG_INDIRECT:
+ if (op->src.type == OP_SRC_REG) {
+ /* st.d _reg, sp, imm */
+ save_reg(cfi, op->src.reg, CFI_CFA, op->dest.offset - cfi->stack_size);
+ }
+ break;
+ default:
+ WARN_FUNC("unknown stack-related instruction", insn->sec, insn->offset);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index 3a1d80a7878d..848d890a3cbf 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -841,3 +841,510 @@ bool arch_is_embedded_insn(struct symbol *sym)
return !strcmp(sym->name, "retbleed_return_thunk") ||
!strcmp(sym->name, "srso_safe_ret");
}
+
+static int update_cfi_state_regs(struct instruction *insn,
+ struct cfi_state *cfi,
+ struct stack_op *op)
+{
+ struct cfi_reg *cfa = &cfi->cfa;
+
+ if (cfa->base != CFI_SP && cfa->base != CFI_SP_INDIRECT)
+ return 0;
+
+ /* push */
+ if (op->dest.type == OP_DEST_PUSH || op->dest.type == OP_DEST_PUSHF)
+ cfa->offset += 8;
+
+ /* pop */
+ if (op->src.type == OP_SRC_POP || op->src.type == OP_SRC_POPF)
+ cfa->offset -= 8;
+
+ /* add immediate to sp */
+ if (op->dest.type == OP_DEST_REG && op->src.type == OP_SRC_ADD &&
+ op->dest.reg == CFI_SP && op->src.reg == CFI_SP)
+ cfa->offset -= op->src.offset;
+
+ return 0;
+}
+
+static void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int offset)
+{
+ if (arch_callee_saved_reg(reg) &&
+ cfi->regs[reg].base == CFI_UNDEFINED) {
+ cfi->regs[reg].base = base;
+ cfi->regs[reg].offset = offset;
+ }
+}
+
+static void restore_reg(struct cfi_state *cfi, unsigned char reg)
+{
+ cfi->regs[reg].base = initial_func_cfi.regs[reg].base;
+ cfi->regs[reg].offset = initial_func_cfi.regs[reg].offset;
+}
+
+/*
+ * A note about DRAP stack alignment:
+ *
+ * GCC has the concept of a DRAP register, which is used to help keep track of
+ * the stack pointer when aligning the stack. r10 or r13 is used as the DRAP
+ * register. The typical DRAP pattern is:
+ *
+ * 4c 8d 54 24 08 lea 0x8(%rsp),%r10
+ * 48 83 e4 c0 and $0xffffffffffffffc0,%rsp
+ * 41 ff 72 f8 pushq -0x8(%r10)
+ * 55 push %rbp
+ * 48 89 e5 mov %rsp,%rbp
+ * (more pushes)
+ * 41 52 push %r10
+ * ...
+ * 41 5a pop %r10
+ * (more pops)
+ * 5d pop %rbp
+ * 49 8d 62 f8 lea -0x8(%r10),%rsp
+ * c3 retq
+ *
+ * There are some variations in the epilogues, like:
+ *
+ * 5b pop %rbx
+ * 41 5a pop %r10
+ * 41 5c pop %r12
+ * 41 5d pop %r13
+ * 41 5e pop %r14
+ * c9 leaveq
+ * 49 8d 62 f8 lea -0x8(%r10),%rsp
+ * c3 retq
+ *
+ * and:
+ *
+ * 4c 8b 55 e8 mov -0x18(%rbp),%r10
+ * 48 8b 5d e0 mov -0x20(%rbp),%rbx
+ * 4c 8b 65 f0 mov -0x10(%rbp),%r12
+ * 4c 8b 6d f8 mov -0x8(%rbp),%r13
+ * c9 leaveq
+ * 49 8d 62 f8 lea -0x8(%r10),%rsp
+ * c3 retq
+ *
+ * Sometimes r13 is used as the DRAP register, in which case it's saved and
+ * restored beforehand:
+ *
+ * 41 55 push %r13
+ * 4c 8d 6c 24 10 lea 0x10(%rsp),%r13
+ * 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
+ * ...
+ * 49 8d 65 f0 lea -0x10(%r13),%rsp
+ * 41 5d pop %r13
+ * c3 retq
+ */
+int arch_update_cfi_state(struct instruction *insn,
+ struct instruction *next_insn,
+ struct cfi_state *cfi, struct stack_op *op)
+{
+ struct cfi_reg *cfa = &cfi->cfa;
+ struct cfi_reg *regs = cfi->regs;
+
+ /* ignore UNWIND_HINT_UNDEFINED regions */
+ if (cfi->force_undefined)
+ return 0;
+
+ /* stack operations don't make sense with an undefined CFA */
+ if (cfa->base == CFI_UNDEFINED) {
+ if (insn_func(insn)) {
+ WARN_INSN(insn, "undefined stack state");
+ return -1;
+ }
+ return 0;
+ }
+
+ if (cfi->type == UNWIND_HINT_TYPE_REGS ||
+ cfi->type == UNWIND_HINT_TYPE_REGS_PARTIAL)
+ return update_cfi_state_regs(insn, cfi, op);
+
+ switch (op->dest.type) {
+
+ case OP_DEST_REG:
+ switch (op->src.type) {
+
+ case OP_SRC_REG:
+ if (op->src.reg == CFI_SP && op->dest.reg == CFI_BP &&
+ cfa->base == CFI_SP &&
+ check_reg_frame_pos(®s[CFI_BP], -cfa->offset)) {
+
+ /* mov %rsp, %rbp */
+ cfa->base = op->dest.reg;
+ cfi->bp_scratch = false;
+ }
+
+ else if (op->src.reg == CFI_SP &&
+ op->dest.reg == CFI_BP && cfi->drap) {
+
+ /* drap: mov %rsp, %rbp */
+ regs[CFI_BP].base = CFI_BP;
+ regs[CFI_BP].offset = -cfi->stack_size;
+ cfi->bp_scratch = false;
+ }
+
+ else if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
+
+ /*
+ * mov %rsp, %reg
+ *
+ * This is needed for the rare case where GCC
+ * does:
+ *
+ * mov %rsp, %rax
+ * ...
+ * mov %rax, %rsp
+ */
+ cfi->vals[op->dest.reg].base = CFI_CFA;
+ cfi->vals[op->dest.reg].offset = -cfi->stack_size;
+ }
+
+ else if (op->src.reg == CFI_BP && op->dest.reg == CFI_SP &&
+ (cfa->base == CFI_BP || cfa->base == cfi->drap_reg)) {
+
+ /*
+ * mov %rbp, %rsp
+ *
+ * Restore the original stack pointer (Clang).
+ */
+ cfi->stack_size = -cfi->regs[CFI_BP].offset;
+ }
+
+ else if (op->dest.reg == cfa->base) {
+
+ /* mov %reg, %rsp */
+ if (cfa->base == CFI_SP &&
+ cfi->vals[op->src.reg].base == CFI_CFA) {
+
+ /*
+ * This is needed for the rare case
+ * where GCC does something dumb like:
+ *
+ * lea 0x8(%rsp), %rcx
+ * ...
+ * mov %rcx, %rsp
+ */
+ cfa->offset = -cfi->vals[op->src.reg].offset;
+ cfi->stack_size = cfa->offset;
+
+ } else if (cfa->base == CFI_SP &&
+ cfi->vals[op->src.reg].base == CFI_SP_INDIRECT &&
+ cfi->vals[op->src.reg].offset == cfa->offset) {
+
+ /*
+ * Stack swizzle:
+ *
+ * 1: mov %rsp, (%[tos])
+ * 2: mov %[tos], %rsp
+ * ...
+ * 3: pop %rsp
+ *
+ * Where:
+ *
+ * 1 - places a pointer to the previous
+ * stack at the Top-of-Stack of the
+ * new stack.
+ *
+ * 2 - switches to the new stack.
+ *
+ * 3 - pops the Top-of-Stack to restore
+ * the original stack.
+ *
+ * Note: we set base to SP_INDIRECT
+ * here and preserve offset. Therefore
+ * when the unwinder reaches ToS it
+ * will dereference SP and then add the
+ * offset to find the next frame, IOW:
+ * (%rsp) + offset.
+ */
+ cfa->base = CFI_SP_INDIRECT;
+
+ } else {
+ cfa->base = CFI_UNDEFINED;
+ cfa->offset = 0;
+ }
+ }
+
+ else if (op->dest.reg == CFI_SP &&
+ cfi->vals[op->src.reg].base == CFI_SP_INDIRECT &&
+ cfi->vals[op->src.reg].offset == cfa->offset) {
+
+ /*
+ * The same stack swizzle case 2) as above. But
+ * because we can't change cfa->base, case 3)
+ * will become a regular POP. Pretend we're a
+ * PUSH so things don't go unbalanced.
+ */
+ cfi->stack_size += 8;
+ }
+
+
+ break;
+
+ case OP_SRC_ADD:
+ if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) {
+
+ /* add imm, %rsp */
+ cfi->stack_size -= op->src.offset;
+ if (cfa->base == CFI_SP)
+ cfa->offset -= op->src.offset;
+ break;
+ }
+
+ if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) {
+
+ /* lea disp(%rbp), %rsp */
+ cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset);
+ break;
+ }
+
+ if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
+
+ /* drap: lea disp(%rsp), %drap */
+ cfi->drap_reg = op->dest.reg;
+
+ /*
+ * lea disp(%rsp), %reg
+ *
+ * This is needed for the rare case where GCC
+ * does something dumb like:
+ *
+ * lea 0x8(%rsp), %rcx
+ * ...
+ * mov %rcx, %rsp
+ */
+ cfi->vals[op->dest.reg].base = CFI_CFA;
+ cfi->vals[op->dest.reg].offset = \
+ -cfi->stack_size + op->src.offset;
+
+ break;
+ }
+
+ if (cfi->drap && op->dest.reg == CFI_SP &&
+ op->src.reg == cfi->drap_reg) {
+
+ /* drap: lea disp(%drap), %rsp */
+ cfa->base = CFI_SP;
+ cfa->offset = cfi->stack_size = -op->src.offset;
+ cfi->drap_reg = CFI_UNDEFINED;
+ cfi->drap = false;
+ break;
+ }
+
+ if (op->dest.reg == cfi->cfa.base && !(next_insn && next_insn->hint)) {
+ WARN_INSN(insn, "unsupported stack register modification");
+ return -1;
+ }
+
+ break;
+
+ case OP_SRC_AND:
+ if (op->dest.reg != CFI_SP ||
+ (cfi->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
+ (cfi->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
+ WARN_INSN(insn, "unsupported stack pointer realignment");
+ return -1;
+ }
+
+ if (cfi->drap_reg != CFI_UNDEFINED) {
+ /* drap: and imm, %rsp */
+ cfa->base = cfi->drap_reg;
+ cfa->offset = cfi->stack_size = 0;
+ cfi->drap = true;
+ }
+
+ /*
+ * Older versions of GCC (4.8ish) realign the stack
+ * without DRAP, with a frame pointer.
+ */
+
+ break;
+
+ case OP_SRC_POP:
+ case OP_SRC_POPF:
+ if (op->dest.reg == CFI_SP && cfa->base == CFI_SP_INDIRECT) {
+
+ /* pop %rsp; # restore from a stack swizzle */
+ cfa->base = CFI_SP;
+ break;
+ }
+
+ if (!cfi->drap && op->dest.reg == cfa->base) {
+
+ /* pop %rbp */
+ cfa->base = CFI_SP;
+ }
+
+ if (cfi->drap && cfa->base == CFI_BP_INDIRECT &&
+ op->dest.reg == cfi->drap_reg &&
+ cfi->drap_offset == -cfi->stack_size) {
+
+ /* drap: pop %drap */
+ cfa->base = cfi->drap_reg;
+ cfa->offset = 0;
+ cfi->drap_offset = -1;
+
+ } else if (cfi->stack_size == -regs[op->dest.reg].offset) {
+
+ /* pop %reg */
+ restore_reg(cfi, op->dest.reg);
+ }
+
+ cfi->stack_size -= 8;
+ if (cfa->base == CFI_SP)
+ cfa->offset -= 8;
+
+ break;
+
+ case OP_SRC_REG_INDIRECT:
+ if (!cfi->drap && op->dest.reg == cfa->base &&
+ op->dest.reg == CFI_BP) {
+
+ /* mov disp(%rsp), %rbp */
+ cfa->base = CFI_SP;
+ cfa->offset = cfi->stack_size;
+ }
+
+ if (cfi->drap && op->src.reg == CFI_BP &&
+ op->src.offset == cfi->drap_offset) {
+
+ /* drap: mov disp(%rbp), %drap */
+ cfa->base = cfi->drap_reg;
+ cfa->offset = 0;
+ cfi->drap_offset = -1;
+ }
+
+ if (cfi->drap && op->src.reg == CFI_BP &&
+ op->src.offset == regs[op->dest.reg].offset) {
+
+ /* drap: mov disp(%rbp), %reg */
+ restore_reg(cfi, op->dest.reg);
+
+ } else if (op->src.reg == cfa->base &&
+ op->src.offset == regs[op->dest.reg].offset + cfa->offset) {
+
+ /* mov disp(%rbp), %reg */
+ /* mov disp(%rsp), %reg */
+ restore_reg(cfi, op->dest.reg);
+
+ } else if (op->src.reg == CFI_SP &&
+ op->src.offset == regs[op->dest.reg].offset + cfi->stack_size) {
+
+ /* mov disp(%rsp), %reg */
+ restore_reg(cfi, op->dest.reg);
+ }
+
+ break;
+
+ default:
+ WARN_INSN(insn, "unknown stack-related instruction");
+ return -1;
+ }
+
+ break;
+
+ case OP_DEST_PUSH:
+ case OP_DEST_PUSHF:
+ cfi->stack_size += 8;
+ if (cfa->base == CFI_SP)
+ cfa->offset += 8;
+
+ if (op->src.type != OP_SRC_REG)
+ break;
+
+ if (cfi->drap) {
+ if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
+
+ /* drap: push %drap */
+ cfa->base = CFI_BP_INDIRECT;
+ cfa->offset = -cfi->stack_size;
+
+ /* save drap so we know when to restore it */
+ cfi->drap_offset = -cfi->stack_size;
+
+ } else if (op->src.reg == CFI_BP && cfa->base == cfi->drap_reg) {
+
+ /* drap: push %rbp */
+ cfi->stack_size = 0;
+
+ } else {
+
+ /* drap: push %reg */
+ save_reg(cfi, op->src.reg, CFI_BP, -cfi->stack_size);
+ }
+
+ } else {
+
+ /* push %reg */
+ save_reg(cfi, op->src.reg, CFI_CFA, -cfi->stack_size);
+ }
+
+ /* detect when asm code uses rbp as a scratch register */
+ if (opts.stackval && insn_func(insn) && op->src.reg == CFI_BP &&
+ cfa->base != CFI_BP)
+ cfi->bp_scratch = true;
+ break;
+
+ case OP_DEST_REG_INDIRECT:
+
+ if (cfi->drap) {
+ if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
+
+ /* drap: mov %drap, disp(%rbp) */
+ cfa->base = CFI_BP_INDIRECT;
+ cfa->offset = op->dest.offset;
+
+ /* save drap offset so we know when to restore it */
+ cfi->drap_offset = op->dest.offset;
+ } else {
+
+ /* drap: mov reg, disp(%rbp) */
+ save_reg(cfi, op->src.reg, CFI_BP, op->dest.offset);
+ }
+
+ } else if (op->dest.reg == cfa->base) {
+
+ /* mov reg, disp(%rbp) */
+ /* mov reg, disp(%rsp) */
+ save_reg(cfi, op->src.reg, CFI_CFA,
+ op->dest.offset - cfi->cfa.offset);
+
+ } else if (op->dest.reg == CFI_SP) {
+
+ /* mov reg, disp(%rsp) */
+ save_reg(cfi, op->src.reg, CFI_CFA,
+ op->dest.offset - cfi->stack_size);
+
+ } else if (op->src.reg == CFI_SP && op->dest.offset == 0) {
+
+ /* mov %rsp, (%reg); # setup a stack swizzle. */
+ cfi->vals[op->dest.reg].base = CFI_SP_INDIRECT;
+ cfi->vals[op->dest.reg].offset = cfa->offset;
+ }
+
+ break;
+
+ case OP_DEST_MEM:
+ if (op->src.type != OP_SRC_POP && op->src.type != OP_SRC_POPF) {
+ WARN_INSN(insn, "unknown stack-related memory operation");
+ return -1;
+ }
+
+ /* pop mem */
+ cfi->stack_size -= 8;
+ if (cfa->base == CFI_SP)
+ cfa->offset -= 8;
+
+ break;
+
+ default:
+ WARN_INSN(insn, "unknown stack-related instruction");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 703f99c5a80d..1fe7e21a75be 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2753,509 +2753,6 @@ static bool has_valid_stack_frame(struct insn_state *state)
return false;
}
-static int update_cfi_state_regs(struct instruction *insn,
- struct cfi_state *cfi,
- struct stack_op *op)
-{
- struct cfi_reg *cfa = &cfi->cfa;
-
- if (cfa->base != CFI_SP && cfa->base != CFI_SP_INDIRECT)
- return 0;
-
- /* push */
- if (op->dest.type == OP_DEST_PUSH || op->dest.type == OP_DEST_PUSHF)
- cfa->offset += 8;
-
- /* pop */
- if (op->src.type == OP_SRC_POP || op->src.type == OP_SRC_POPF)
- cfa->offset -= 8;
-
- /* add immediate to sp */
- if (op->dest.type == OP_DEST_REG && op->src.type == OP_SRC_ADD &&
- op->dest.reg == CFI_SP && op->src.reg == CFI_SP)
- cfa->offset -= op->src.offset;
-
- return 0;
-}
-
-static void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int offset)
-{
- if (arch_callee_saved_reg(reg) &&
- cfi->regs[reg].base == CFI_UNDEFINED) {
- cfi->regs[reg].base = base;
- cfi->regs[reg].offset = offset;
- }
-}
-
-static void restore_reg(struct cfi_state *cfi, unsigned char reg)
-{
- cfi->regs[reg].base = initial_func_cfi.regs[reg].base;
- cfi->regs[reg].offset = initial_func_cfi.regs[reg].offset;
-}
-
-/*
- * A note about DRAP stack alignment:
- *
- * GCC has the concept of a DRAP register, which is used to help keep track of
- * the stack pointer when aligning the stack. r10 or r13 is used as the DRAP
- * register. The typical DRAP pattern is:
- *
- * 4c 8d 54 24 08 lea 0x8(%rsp),%r10
- * 48 83 e4 c0 and $0xffffffffffffffc0,%rsp
- * 41 ff 72 f8 pushq -0x8(%r10)
- * 55 push %rbp
- * 48 89 e5 mov %rsp,%rbp
- * (more pushes)
- * 41 52 push %r10
- * ...
- * 41 5a pop %r10
- * (more pops)
- * 5d pop %rbp
- * 49 8d 62 f8 lea -0x8(%r10),%rsp
- * c3 retq
- *
- * There are some variations in the epilogues, like:
- *
- * 5b pop %rbx
- * 41 5a pop %r10
- * 41 5c pop %r12
- * 41 5d pop %r13
- * 41 5e pop %r14
- * c9 leaveq
- * 49 8d 62 f8 lea -0x8(%r10),%rsp
- * c3 retq
- *
- * and:
- *
- * 4c 8b 55 e8 mov -0x18(%rbp),%r10
- * 48 8b 5d e0 mov -0x20(%rbp),%rbx
- * 4c 8b 65 f0 mov -0x10(%rbp),%r12
- * 4c 8b 6d f8 mov -0x8(%rbp),%r13
- * c9 leaveq
- * 49 8d 62 f8 lea -0x8(%r10),%rsp
- * c3 retq
- *
- * Sometimes r13 is used as the DRAP register, in which case it's saved and
- * restored beforehand:
- *
- * 41 55 push %r13
- * 4c 8d 6c 24 10 lea 0x10(%rsp),%r13
- * 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
- * ...
- * 49 8d 65 f0 lea -0x10(%r13),%rsp
- * 41 5d pop %r13
- * c3 retq
- */
-static int update_cfi_state(struct instruction *insn,
- struct instruction *next_insn,
- struct cfi_state *cfi, struct stack_op *op)
-{
- struct cfi_reg *cfa = &cfi->cfa;
- struct cfi_reg *regs = cfi->regs;
-
- /* ignore UNWIND_HINT_UNDEFINED regions */
- if (cfi->force_undefined)
- return 0;
-
- /* stack operations don't make sense with an undefined CFA */
- if (cfa->base == CFI_UNDEFINED) {
- if (insn_func(insn)) {
- WARN_INSN(insn, "undefined stack state");
- return -1;
- }
- return 0;
- }
-
- if (cfi->type == UNWIND_HINT_TYPE_REGS ||
- cfi->type == UNWIND_HINT_TYPE_REGS_PARTIAL)
- return update_cfi_state_regs(insn, cfi, op);
-
- switch (op->dest.type) {
-
- case OP_DEST_REG:
- switch (op->src.type) {
-
- case OP_SRC_REG:
- if (op->src.reg == CFI_SP && op->dest.reg == CFI_BP &&
- cfa->base == CFI_SP &&
- check_reg_frame_pos(®s[CFI_BP], -cfa->offset)) {
-
- /* mov %rsp, %rbp */
- cfa->base = op->dest.reg;
- cfi->bp_scratch = false;
- }
-
- else if (op->src.reg == CFI_SP &&
- op->dest.reg == CFI_BP && cfi->drap) {
-
- /* drap: mov %rsp, %rbp */
- regs[CFI_BP].base = CFI_BP;
- regs[CFI_BP].offset = -cfi->stack_size;
- cfi->bp_scratch = false;
- }
-
- else if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
-
- /*
- * mov %rsp, %reg
- *
- * This is needed for the rare case where GCC
- * does:
- *
- * mov %rsp, %rax
- * ...
- * mov %rax, %rsp
- */
- cfi->vals[op->dest.reg].base = CFI_CFA;
- cfi->vals[op->dest.reg].offset = -cfi->stack_size;
- }
-
- else if (op->src.reg == CFI_BP && op->dest.reg == CFI_SP &&
- (cfa->base == CFI_BP || cfa->base == cfi->drap_reg)) {
-
- /*
- * mov %rbp, %rsp
- *
- * Restore the original stack pointer (Clang).
- */
- cfi->stack_size = -cfi->regs[CFI_BP].offset;
- }
-
- else if (op->dest.reg == cfa->base) {
-
- /* mov %reg, %rsp */
- if (cfa->base == CFI_SP &&
- cfi->vals[op->src.reg].base == CFI_CFA) {
-
- /*
- * This is needed for the rare case
- * where GCC does something dumb like:
- *
- * lea 0x8(%rsp), %rcx
- * ...
- * mov %rcx, %rsp
- */
- cfa->offset = -cfi->vals[op->src.reg].offset;
- cfi->stack_size = cfa->offset;
-
- } else if (cfa->base == CFI_SP &&
- cfi->vals[op->src.reg].base == CFI_SP_INDIRECT &&
- cfi->vals[op->src.reg].offset == cfa->offset) {
-
- /*
- * Stack swizzle:
- *
- * 1: mov %rsp, (%[tos])
- * 2: mov %[tos], %rsp
- * ...
- * 3: pop %rsp
- *
- * Where:
- *
- * 1 - places a pointer to the previous
- * stack at the Top-of-Stack of the
- * new stack.
- *
- * 2 - switches to the new stack.
- *
- * 3 - pops the Top-of-Stack to restore
- * the original stack.
- *
- * Note: we set base to SP_INDIRECT
- * here and preserve offset. Therefore
- * when the unwinder reaches ToS it
- * will dereference SP and then add the
- * offset to find the next frame, IOW:
- * (%rsp) + offset.
- */
- cfa->base = CFI_SP_INDIRECT;
-
- } else {
- cfa->base = CFI_UNDEFINED;
- cfa->offset = 0;
- }
- }
-
- else if (op->dest.reg == CFI_SP &&
- cfi->vals[op->src.reg].base == CFI_SP_INDIRECT &&
- cfi->vals[op->src.reg].offset == cfa->offset) {
-
- /*
- * The same stack swizzle case 2) as above. But
- * because we can't change cfa->base, case 3)
- * will become a regular POP. Pretend we're a
- * PUSH so things don't go unbalanced.
- */
- cfi->stack_size += 8;
- }
-
-
- break;
-
- case OP_SRC_ADD:
- if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) {
-
- /* add imm, %rsp */
- cfi->stack_size -= op->src.offset;
- if (cfa->base == CFI_SP)
- cfa->offset -= op->src.offset;
- break;
- }
-
- if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) {
-
- /* lea disp(%rbp), %rsp */
- cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset);
- break;
- }
-
- if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
-
- /* drap: lea disp(%rsp), %drap */
- cfi->drap_reg = op->dest.reg;
-
- /*
- * lea disp(%rsp), %reg
- *
- * This is needed for the rare case where GCC
- * does something dumb like:
- *
- * lea 0x8(%rsp), %rcx
- * ...
- * mov %rcx, %rsp
- */
- cfi->vals[op->dest.reg].base = CFI_CFA;
- cfi->vals[op->dest.reg].offset = \
- -cfi->stack_size + op->src.offset;
-
- break;
- }
-
- if (cfi->drap && op->dest.reg == CFI_SP &&
- op->src.reg == cfi->drap_reg) {
-
- /* drap: lea disp(%drap), %rsp */
- cfa->base = CFI_SP;
- cfa->offset = cfi->stack_size = -op->src.offset;
- cfi->drap_reg = CFI_UNDEFINED;
- cfi->drap = false;
- break;
- }
-
- if (op->dest.reg == cfi->cfa.base && !(next_insn && next_insn->hint)) {
- WARN_INSN(insn, "unsupported stack register modification");
- return -1;
- }
-
- break;
-
- case OP_SRC_AND:
- if (op->dest.reg != CFI_SP ||
- (cfi->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
- (cfi->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
- WARN_INSN(insn, "unsupported stack pointer realignment");
- return -1;
- }
-
- if (cfi->drap_reg != CFI_UNDEFINED) {
- /* drap: and imm, %rsp */
- cfa->base = cfi->drap_reg;
- cfa->offset = cfi->stack_size = 0;
- cfi->drap = true;
- }
-
- /*
- * Older versions of GCC (4.8ish) realign the stack
- * without DRAP, with a frame pointer.
- */
-
- break;
-
- case OP_SRC_POP:
- case OP_SRC_POPF:
- if (op->dest.reg == CFI_SP && cfa->base == CFI_SP_INDIRECT) {
-
- /* pop %rsp; # restore from a stack swizzle */
- cfa->base = CFI_SP;
- break;
- }
-
- if (!cfi->drap && op->dest.reg == cfa->base) {
-
- /* pop %rbp */
- cfa->base = CFI_SP;
- }
-
- if (cfi->drap && cfa->base == CFI_BP_INDIRECT &&
- op->dest.reg == cfi->drap_reg &&
- cfi->drap_offset == -cfi->stack_size) {
-
- /* drap: pop %drap */
- cfa->base = cfi->drap_reg;
- cfa->offset = 0;
- cfi->drap_offset = -1;
-
- } else if (cfi->stack_size == -regs[op->dest.reg].offset) {
-
- /* pop %reg */
- restore_reg(cfi, op->dest.reg);
- }
-
- cfi->stack_size -= 8;
- if (cfa->base == CFI_SP)
- cfa->offset -= 8;
-
- break;
-
- case OP_SRC_REG_INDIRECT:
- if (!cfi->drap && op->dest.reg == cfa->base &&
- op->dest.reg == CFI_BP) {
-
- /* mov disp(%rsp), %rbp */
- cfa->base = CFI_SP;
- cfa->offset = cfi->stack_size;
- }
-
- if (cfi->drap && op->src.reg == CFI_BP &&
- op->src.offset == cfi->drap_offset) {
-
- /* drap: mov disp(%rbp), %drap */
- cfa->base = cfi->drap_reg;
- cfa->offset = 0;
- cfi->drap_offset = -1;
- }
-
- if (cfi->drap && op->src.reg == CFI_BP &&
- op->src.offset == regs[op->dest.reg].offset) {
-
- /* drap: mov disp(%rbp), %reg */
- restore_reg(cfi, op->dest.reg);
-
- } else if (op->src.reg == cfa->base &&
- op->src.offset == regs[op->dest.reg].offset + cfa->offset) {
-
- /* mov disp(%rbp), %reg */
- /* mov disp(%rsp), %reg */
- restore_reg(cfi, op->dest.reg);
-
- } else if (op->src.reg == CFI_SP &&
- op->src.offset == regs[op->dest.reg].offset + cfi->stack_size) {
-
- /* mov disp(%rsp), %reg */
- restore_reg(cfi, op->dest.reg);
- }
-
- break;
-
- default:
- WARN_INSN(insn, "unknown stack-related instruction");
- return -1;
- }
-
- break;
-
- case OP_DEST_PUSH:
- case OP_DEST_PUSHF:
- cfi->stack_size += 8;
- if (cfa->base == CFI_SP)
- cfa->offset += 8;
-
- if (op->src.type != OP_SRC_REG)
- break;
-
- if (cfi->drap) {
- if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
-
- /* drap: push %drap */
- cfa->base = CFI_BP_INDIRECT;
- cfa->offset = -cfi->stack_size;
-
- /* save drap so we know when to restore it */
- cfi->drap_offset = -cfi->stack_size;
-
- } else if (op->src.reg == CFI_BP && cfa->base == cfi->drap_reg) {
-
- /* drap: push %rbp */
- cfi->stack_size = 0;
-
- } else {
-
- /* drap: push %reg */
- save_reg(cfi, op->src.reg, CFI_BP, -cfi->stack_size);
- }
-
- } else {
-
- /* push %reg */
- save_reg(cfi, op->src.reg, CFI_CFA, -cfi->stack_size);
- }
-
- /* detect when asm code uses rbp as a scratch register */
- if (opts.stackval && insn_func(insn) && op->src.reg == CFI_BP &&
- cfa->base != CFI_BP)
- cfi->bp_scratch = true;
- break;
-
- case OP_DEST_REG_INDIRECT:
-
- if (cfi->drap) {
- if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
-
- /* drap: mov %drap, disp(%rbp) */
- cfa->base = CFI_BP_INDIRECT;
- cfa->offset = op->dest.offset;
-
- /* save drap offset so we know when to restore it */
- cfi->drap_offset = op->dest.offset;
- } else {
-
- /* drap: mov reg, disp(%rbp) */
- save_reg(cfi, op->src.reg, CFI_BP, op->dest.offset);
- }
-
- } else if (op->dest.reg == cfa->base) {
-
- /* mov reg, disp(%rbp) */
- /* mov reg, disp(%rsp) */
- save_reg(cfi, op->src.reg, CFI_CFA,
- op->dest.offset - cfi->cfa.offset);
-
- } else if (op->dest.reg == CFI_SP) {
-
- /* mov reg, disp(%rsp) */
- save_reg(cfi, op->src.reg, CFI_CFA,
- op->dest.offset - cfi->stack_size);
-
- } else if (op->src.reg == CFI_SP && op->dest.offset == 0) {
-
- /* mov %rsp, (%reg); # setup a stack swizzle. */
- cfi->vals[op->dest.reg].base = CFI_SP_INDIRECT;
- cfi->vals[op->dest.reg].offset = cfa->offset;
- }
-
- break;
-
- case OP_DEST_MEM:
- if (op->src.type != OP_SRC_POP && op->src.type != OP_SRC_POPF) {
- WARN_INSN(insn, "unknown stack-related memory operation");
- return -1;
- }
-
- /* pop mem */
- cfi->stack_size -= 8;
- if (cfa->base == CFI_SP)
- cfa->offset -= 8;
-
- break;
-
- default:
- WARN_INSN(insn, "unknown stack-related instruction");
- return -1;
- }
-
- return 0;
-}
-
/*
* The stack layouts of alternatives instructions can sometimes diverge when
* they have stack modifications. That's fine as long as the potential stack
@@ -3305,7 +2802,7 @@ static int handle_insn_ops(struct instruction *insn,
for (op = insn->stack_ops; op; op = op->next) {
- if (update_cfi_state(insn, next_insn, &state->cfi, op))
+ if (arch_update_cfi_state(insn, next_insn, &state->cfi, op))
return 1;
if (!insn->alt_group)
diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h
index 0b303eba660e..c1cf08165e44 100644
--- a/tools/objtool/include/objtool/arch.h
+++ b/tools/objtool/include/objtool/arch.h
@@ -96,4 +96,8 @@ int arch_rewrite_retpolines(struct objtool_file *file);
bool arch_pc_relative_reloc(struct reloc *reloc);
+int arch_update_cfi_state(struct instruction *insn,
+ struct instruction *next_insn,
+ struct cfi_state *cfi, struct stack_op *op);
+
#endif /* _ARCH_H */
--
2.43.0
next prev parent reply other threads:[~2024-06-05 15:47 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-06-04 15:07 Xi Ruoyao
2024-06-05 1:52 ` Huacai Chen
2024-06-05 4:38 ` Xi Ruoyao
2024-06-05 5:21 ` Huacai Chen
2024-06-05 5:25 ` Xi Ruoyao
2024-06-05 2:04 ` Heng Qi
2024-06-05 5:43 ` Nathan Chancellor
2024-06-05 5:54 ` Xi Ruoyao
2024-06-05 6:25 ` Nathan Chancellor
2024-06-05 10:57 ` Xi Ruoyao
2024-06-05 13:18 ` Xi Ruoyao
2024-06-05 15:13 ` Xi Ruoyao
2024-06-05 15:47 ` Jinyang He [this message]
2024-06-05 19:05 ` Xi Ruoyao
2024-06-06 2:10 ` Jinyang He
2024-06-07 5:42 ` Xi Ruoyao
2024-06-07 7:14 ` Jinyang He
2024-06-07 8:29 ` Xi Ruoyao
2024-06-15 8:45 ` Huacai Chen
2024-06-15 8:53 ` Xi Ruoyao
2024-06-15 9:33 ` Huacai Chen
2024-06-15 10:22 ` Xi Ruoyao
2024-06-17 13:11 ` Huacai Chen
2024-06-17 13:38 ` Xi Ruoyao
2024-06-17 13:47 ` Huacai Chen
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=82b7e6ea-c2cb-6364-ebe9-bff928028408@loongson.cn \
--to=hejinyang@loongson.cn \
--cc=cailulu@loongson.cn \
--cc=chenhuacai@kernel.org \
--cc=hengqi@linux.alibaba.com \
--cc=justinstitt@google.com \
--cc=kernel@xen0n.name \
--cc=linux-kernel@vger.kernel.org \
--cc=llvm@lists.linux.dev \
--cc=loongarch@lists.linux.dev \
--cc=luweining@loongson.cn \
--cc=mengqinggang@loongson.cn \
--cc=morbo@google.com \
--cc=nathan@kernel.org \
--cc=ndesaulniers@google.com \
--cc=peterz@infradead.org \
--cc=tangyouling@kylinos.cn \
--cc=tj@kernel.org \
--cc=wanglei@loongson.cn \
--cc=xry111@xry111.site \
--cc=yangtiezhu@loongson.cn \
--cc=yujie.liu@intel.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 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®