2019-05-19 15:51:43 +02:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
2017-06-28 10:11:05 -05:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef _CHECK_H
|
|
|
|
|
#define _CHECK_H
|
|
|
|
|
|
|
|
|
|
#include <stdbool.h>
|
2020-11-13 00:03:32 +01:00
|
|
|
#include <objtool/cfi.h>
|
|
|
|
|
#include <objtool/arch.h>
|
2017-06-28 10:11:05 -05:00
|
|
|
|
2017-06-28 10:11:07 -05:00
|
|
|
struct insn_state {
|
2020-03-25 14:04:45 +01:00
|
|
|
struct cfi_state cfi;
|
2019-02-25 12:50:09 +01:00
|
|
|
unsigned int uaccess_stack;
|
2020-03-25 14:04:45 +01:00
|
|
|
bool uaccess;
|
|
|
|
|
bool df;
|
2020-03-10 18:57:41 +01:00
|
|
|
bool noinstr;
|
|
|
|
|
s8 instr;
|
2017-06-28 10:11:07 -05:00
|
|
|
};
|
|
|
|
|
|
2020-12-18 14:19:32 -06:00
|
|
|
struct alt_group {
|
|
|
|
|
/*
|
|
|
|
|
* Pointer from a replacement group to the original group. NULL if it
|
|
|
|
|
* *is* the original group.
|
|
|
|
|
*/
|
|
|
|
|
struct alt_group *orig_group;
|
|
|
|
|
|
|
|
|
|
/* First and last instructions in the group */
|
|
|
|
|
struct instruction *first_insn, *last_insn;
|
objtool: Support stack layout changes in alternatives
The ORC unwinder showed a warning [1] which revealed the stack layout
didn't match what was expected. The problem was that paravirt patching
had replaced "CALL *pv_ops.irq.save_fl" with "PUSHF;POP". That changed
the stack layout between the PUSHF and the POP, so unwinding from an
interrupt which occurred between those two instructions would fail.
Part of the agreed upon solution was to rework the custom paravirt
patching code to use alternatives instead, since objtool already knows
how to read alternatives (and converging runtime patching infrastructure
is always a good thing anyway). But the main problem still remains,
which is that runtime patching can change the stack layout.
Making stack layout changes in alternatives was disallowed with commit
7117f16bf460 ("objtool: Fix ORC vs alternatives"), but now that paravirt
is going to be doing it, it needs to be supported.
One way to do so would be to modify the ORC table when the code gets
patched. But ORC is simple -- a good thing! -- and it's best to leave
it alone.
Instead, support stack layout changes by "flattening" all possible stack
states (CFI) from parallel alternative code streams into a single set of
linear states. The only necessary limitation is that CFI conflicts are
disallowed at all possible instruction boundaries.
For example, this scenario is allowed:
Alt1 Alt2 Alt3
0x00 CALL *pv_ops.save_fl CALL xen_save_fl PUSHF
0x01 POP %RAX
0x02 NOP
...
0x05 NOP
...
0x07 <insn>
The unwind information for offset-0x00 is identical for all 3
alternatives. Similarly offset-0x05 and higher also are identical (and
the same as 0x00). However offset-0x01 has deviating CFI, but that is
only relevant for Alt3, neither of the other alternative instruction
streams will ever hit that offset.
This scenario is NOT allowed:
Alt1 Alt2
0x00 CALL *pv_ops.save_fl PUSHF
0x01 NOP6
...
0x07 NOP POP %RAX
The problem here is that offset-0x7, which is an instruction boundary in
both possible instruction patch streams, has two conflicting stack
layouts.
[ The above examples were stolen from Peter Zijlstra. ]
The new flattened CFI array is used both for the detection of conflicts
(like the second example above) and the generation of linear ORC
entries.
BTW, another benefit of these changes is that, thanks to some related
cleanups (new fake nops and alt_group struct) objtool can finally be rid
of fake jumps, which were a constant source of headaches.
[1] https://lkml.kernel.org/r/20201111170536.arx2zbn4ngvjoov7@treble
Cc: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-12-18 14:26:21 -06:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Byte-offset-addressed len-sized array of pointers to CFI structs.
|
|
|
|
|
* This is shared with the other alt_groups in the same alternative.
|
|
|
|
|
*/
|
|
|
|
|
struct cfi_state **cfi;
|
2020-12-18 14:19:32 -06:00
|
|
|
};
|
|
|
|
|
|
2017-06-28 10:11:05 -05:00
|
|
|
struct instruction {
|
|
|
|
|
struct list_head list;
|
|
|
|
|
struct hlist_node hash;
|
2021-03-26 16:12:12 +01:00
|
|
|
struct list_head call_node;
|
2017-06-28 10:11:05 -05:00
|
|
|
struct section *sec;
|
|
|
|
|
unsigned long offset;
|
2017-06-28 10:11:07 -05:00
|
|
|
unsigned int len;
|
2019-07-17 20:36:56 -05:00
|
|
|
enum insn_type type;
|
2017-06-28 10:11:05 -05:00
|
|
|
unsigned long immediate;
|
2022-03-08 16:30:52 +01:00
|
|
|
|
|
|
|
|
u8 dead_end : 1,
|
|
|
|
|
ignore : 1,
|
|
|
|
|
ignore_alts : 1,
|
|
|
|
|
hint : 1,
|
|
|
|
|
retpoline_safe : 1,
|
|
|
|
|
noendbr : 1;
|
|
|
|
|
/* 2 bit hole */
|
2020-03-10 18:57:41 +01:00
|
|
|
s8 instr;
|
2019-07-24 17:47:26 -05:00
|
|
|
u8 visited;
|
2022-03-08 16:30:52 +01:00
|
|
|
/* u8 hole */
|
|
|
|
|
|
2020-12-18 14:19:32 -06:00
|
|
|
struct alt_group *alt_group;
|
2017-06-28 10:11:05 -05:00
|
|
|
struct symbol *call_dest;
|
|
|
|
|
struct instruction *jump_dest;
|
2018-02-08 14:02:32 +01:00
|
|
|
struct instruction *first_jump_src;
|
objtool: Rename rela to reloc
Before supporting additional relocation types rename the relevant
types and functions from "rela" to "reloc". This work be done with
the following regex:
sed -e 's/struct rela/struct reloc/g' \
-e 's/\([_\*]\)rela\(s\{0,1\}\)/\1reloc\2/g' \
-e 's/tmprela\(s\{0,1\}\)/tmpreloc\1/g' \
-e 's/relasec/relocsec/g' \
-e 's/rela_list/reloc_list/g' \
-e 's/rela_hash/reloc_hash/g' \
-e 's/add_rela/add_reloc/g' \
-e 's/rela->/reloc->/g' \
-e '/rela[,\.]/{ s/\([^\.>]\)rela\([\.,]\)/\1reloc\2/g ; }' \
-e 's/rela =/reloc =/g' \
-e 's/relas =/relocs =/g' \
-e 's/relas\[/relocs[/g' \
-e 's/relaname =/relocname =/g' \
-e 's/= rela\;/= reloc\;/g' \
-e 's/= relas\;/= relocs\;/g' \
-e 's/= relaname\;/= relocname\;/g' \
-e 's/, rela)/, reloc)/g' \
-e 's/\([ @]\)rela\([ "]\)/\1reloc\2/g' \
-e 's/ rela$/ reloc/g' \
-e 's/, relaname/, relocname/g' \
-e 's/sec->rela/sec->reloc/g' \
-e 's/(\(!\{0,1\}\)rela/(\1reloc/g' \
-i \
arch.h \
arch/x86/decode.c \
check.c \
check.h \
elf.c \
elf.h \
orc_gen.c \
special.c
Notable exceptions which complicate the regex include gelf_*
library calls and standard/expected section names which still use
"rela" because they encode the type of relocation expected. Also, keep
"rela" in the struct because it encodes a specific type of relocation
we currently expect.
It will eventually turn into a member of an anonymous union when a
susequent patch adds implicit addend, or "rel", relocation support.
Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
2020-05-29 14:01:13 -07:00
|
|
|
struct reloc *jump_table;
|
2021-03-26 16:12:13 +01:00
|
|
|
struct reloc *reloc;
|
2017-06-28 10:11:05 -05:00
|
|
|
struct list_head alts;
|
|
|
|
|
struct symbol *func;
|
2020-03-27 15:28:47 +00:00
|
|
|
struct list_head stack_ops;
|
2021-06-24 11:41:01 +02:00
|
|
|
struct cfi_state *cfi;
|
2017-06-28 10:11:05 -05:00
|
|
|
};
|
|
|
|
|
|
2020-09-04 16:30:23 +01:00
|
|
|
static inline bool is_static_jump(struct instruction *insn)
|
|
|
|
|
{
|
|
|
|
|
return insn->type == INSN_JUMP_CONDITIONAL ||
|
|
|
|
|
insn->type == INSN_JUMP_UNCONDITIONAL;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-21 15:29:18 -06:00
|
|
|
static inline bool is_dynamic_jump(struct instruction *insn)
|
|
|
|
|
{
|
|
|
|
|
return insn->type == INSN_JUMP_DYNAMIC ||
|
|
|
|
|
insn->type == INSN_JUMP_DYNAMIC_CONDITIONAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool is_jump(struct instruction *insn)
|
|
|
|
|
{
|
|
|
|
|
return is_static_jump(insn) || is_dynamic_jump(insn);
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-11 10:33:42 -05:00
|
|
|
struct instruction *find_insn(struct objtool_file *file,
|
|
|
|
|
struct section *sec, unsigned long offset);
|
2017-06-28 10:11:05 -05:00
|
|
|
|
2017-06-28 10:11:07 -05:00
|
|
|
#define for_each_insn(file, insn) \
|
|
|
|
|
list_for_each_entry(insn, &file->insn_list, list)
|
|
|
|
|
|
2017-07-11 10:33:42 -05:00
|
|
|
#define sec_for_each_insn(file, sec, insn) \
|
|
|
|
|
for (insn = find_insn(file, sec, 0); \
|
|
|
|
|
insn && &insn->list != &file->insn_list && \
|
|
|
|
|
insn->sec == sec; \
|
|
|
|
|
insn = list_next_entry(insn, list))
|
|
|
|
|
|
2017-06-28 10:11:05 -05:00
|
|
|
#endif /* _CHECK_H */
|