objtool: Support stack-swizzle
authorPeter Zijlstra <peterz@infradead.org>
Wed, 3 Feb 2021 11:02:17 +0000 (12:02 +0100)
committerPeter Zijlstra <peterz@infradead.org>
Wed, 10 Feb 2021 19:53:52 +0000 (20:53 +0100)
Natively support the stack swizzle pattern:

mov %rsp, (%[tos])
mov %[tos], %rsp
...
pop %rsp

It uses the vals[] array to link the first two stack-ops, and detect
the SP to SP_INDIRECT swizzle.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
tools/objtool/check.c

index 5f056dd..62cd211 100644 (file)
@@ -1945,6 +1945,38 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
                                        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;
@@ -2047,6 +2079,13 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
 
                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 */
@@ -2193,6 +2232,12 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
                        /* 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;