objtool: Support stack-swizzle

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>
This commit is contained in:
Peter Zijlstra 2021-02-03 12:02:17 +01:00
parent 2a51282984
commit aafeb14e9d
1 changed files with 45 additions and 0 deletions

View 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; cfa->offset = -cfi->vals[op->src.reg].offset;
cfi->stack_size = cfa->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 { } else {
cfa->base = CFI_UNDEFINED; cfa->base = CFI_UNDEFINED;
cfa->offset = 0; 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_POP:
case OP_SRC_POPF: 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) { if (!cfi->drap && op->dest.reg == cfa->base) {
/* pop %rbp */ /* pop %rbp */
@ -2193,6 +2232,12 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
/* mov reg, disp(%rsp) */ /* mov reg, disp(%rsp) */
save_reg(cfi, op->src.reg, CFI_CFA, save_reg(cfi, op->src.reg, CFI_CFA,
op->dest.offset - cfi->stack_size); 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; break;