summaryrefslogtreecommitdiff
path: root/tools/objtool/check.c
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2023-03-09 22:18:59 -0800
committerJakub Kicinski <kuba@kernel.org>2023-03-09 22:22:11 -0800
commitd0ddf5065ffef45f8fce4001abe0206081c7ff10 (patch)
treeea83817cbe9fc25261eae87b85afd9fe086f479e /tools/objtool/check.c
parentdb47fa2e4cbf180a39d8e6d6170962bd7d82e52d (diff)
parent44889ba56cbb3d51154660ccd15818bc77276696 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Documentation/bpf/bpf_devel_QA.rst b7abcd9c656b ("bpf, doc: Link to submitting-patches.rst for general patch submission info") d56b0c461d19 ("bpf, docs: Fix link to netdev-FAQ target") https://lore.kernel.org/all/20230307095812.236eb1be@canb.auug.org.au/ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'tools/objtool/check.c')
-rw-r--r--tools/objtool/check.c375
1 files changed, 231 insertions, 144 deletions
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 35374812afdc..f937be1afe65 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -23,7 +23,7 @@
#include <linux/static_call_types.h>
struct alternative {
- struct list_head list;
+ struct alternative *next;
struct instruction *insn;
bool skip_orig;
};
@@ -47,27 +47,29 @@ struct instruction *find_insn(struct objtool_file *file,
return NULL;
}
-static struct instruction *next_insn_same_sec(struct objtool_file *file,
- struct instruction *insn)
+struct instruction *next_insn_same_sec(struct objtool_file *file,
+ struct instruction *insn)
{
- struct instruction *next = list_next_entry(insn, list);
+ if (insn->idx == INSN_CHUNK_MAX)
+ return find_insn(file, insn->sec, insn->offset + insn->len);
- if (!next || &next->list == &file->insn_list || next->sec != insn->sec)
+ insn++;
+ if (!insn->len)
return NULL;
- return next;
+ return insn;
}
static struct instruction *next_insn_same_func(struct objtool_file *file,
struct instruction *insn)
{
- struct instruction *next = list_next_entry(insn, list);
+ struct instruction *next = next_insn_same_sec(file, insn);
struct symbol *func = insn_func(insn);
if (!func)
return NULL;
- if (&next->list != &file->insn_list && insn_func(next) == func)
+ if (next && insn_func(next) == func)
return next;
/* Check if we're already in the subfunction: */
@@ -78,17 +80,35 @@ static struct instruction *next_insn_same_func(struct objtool_file *file,
return find_insn(file, func->cfunc->sec, func->cfunc->offset);
}
+static struct instruction *prev_insn_same_sec(struct objtool_file *file,
+ struct instruction *insn)
+{
+ if (insn->idx == 0) {
+ if (insn->prev_len)
+ return find_insn(file, insn->sec, insn->offset - insn->prev_len);
+ return NULL;
+ }
+
+ return insn - 1;
+}
+
static struct instruction *prev_insn_same_sym(struct objtool_file *file,
- struct instruction *insn)
+ struct instruction *insn)
{
- struct instruction *prev = list_prev_entry(insn, list);
+ struct instruction *prev = prev_insn_same_sec(file, insn);
- if (&prev->list != &file->insn_list && insn_func(prev) == insn_func(insn))
+ if (prev && insn_func(prev) == insn_func(insn))
return prev;
return NULL;
}
+#define for_each_insn(file, insn) \
+ for (struct section *__sec, *__fake = (struct section *)1; \
+ __fake; __fake = NULL) \
+ for_each_sec(file, __sec) \
+ sec_for_each_insn(file, __sec, insn)
+
#define func_for_each_insn(file, func, insn) \
for (insn = find_insn(file, func->sec, func->offset); \
insn; \
@@ -96,16 +116,13 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,
#define sym_for_each_insn(file, sym, insn) \
for (insn = find_insn(file, sym->sec, sym->offset); \
- insn && &insn->list != &file->insn_list && \
- insn->sec == sym->sec && \
- insn->offset < sym->offset + sym->len; \
- insn = list_next_entry(insn, list))
+ insn && insn->offset < sym->offset + sym->len; \
+ insn = next_insn_same_sec(file, insn))
#define sym_for_each_insn_continue_reverse(file, sym, insn) \
- for (insn = list_prev_entry(insn, list); \
- &insn->list != &file->insn_list && \
- insn->sec == sym->sec && insn->offset >= sym->offset; \
- insn = list_prev_entry(insn, list))
+ for (insn = prev_insn_same_sec(file, insn); \
+ insn && insn->offset >= sym->offset; \
+ insn = prev_insn_same_sec(file, insn))
#define sec_for_each_insn_from(file, insn) \
for (; insn; insn = next_insn_same_sec(file, insn))
@@ -114,16 +131,34 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,
for (insn = next_insn_same_sec(file, insn); insn; \
insn = next_insn_same_sec(file, insn))
+static inline struct symbol *insn_call_dest(struct instruction *insn)
+{
+ if (insn->type == INSN_JUMP_DYNAMIC ||
+ insn->type == INSN_CALL_DYNAMIC)
+ return NULL;
+
+ return insn->_call_dest;
+}
+
+static inline struct reloc *insn_jump_table(struct instruction *insn)
+{
+ if (insn->type == INSN_JUMP_DYNAMIC ||
+ insn->type == INSN_CALL_DYNAMIC)
+ return insn->_jump_table;
+
+ return NULL;
+}
+
static bool is_jump_table_jump(struct instruction *insn)
{
struct alt_group *alt_group = insn->alt_group;
- if (insn->jump_table)
+ if (insn_jump_table(insn))
return true;
/* Retpoline alternative for a jump table? */
return alt_group && alt_group->orig_group &&
- alt_group->orig_group->first_insn->jump_table;
+ insn_jump_table(alt_group->orig_group->first_insn);
}
static bool is_sibling_call(struct instruction *insn)
@@ -137,8 +172,8 @@ static bool is_sibling_call(struct instruction *insn)
return !is_jump_table_jump(insn);
}
- /* add_jump_destinations() sets insn->call_dest for sibling calls. */
- return (is_static_jump(insn) && insn->call_dest);
+ /* add_jump_destinations() sets insn_call_dest(insn) for sibling calls. */
+ return (is_static_jump(insn) && insn_call_dest(insn));
}
/*
@@ -274,8 +309,8 @@ static void init_insn_state(struct objtool_file *file, struct insn_state *state,
/*
* We need the full vmlinux for noinstr validation, otherwise we can
- * not correctly determine insn->call_dest->sec (external symbols do
- * not have a section).
+ * not correctly determine insn_call_dest(insn)->sec (external symbols
+ * do not have a section).
*/
if (opts.link && opts.noinstr && sec)
state->noinstr = sec->noinstr;
@@ -366,6 +401,9 @@ static int decode_instructions(struct objtool_file *file)
int ret;
for_each_sec(file, sec) {
+ struct instruction *insns = NULL;
+ u8 prev_len = 0;
+ u8 idx = 0;
if (!(sec->sh.sh_flags & SHF_EXECINSTR))
continue;
@@ -391,26 +429,31 @@ static int decode_instructions(struct objtool_file *file)
sec->init = true;
for (offset = 0; offset < sec->sh.sh_size; offset += insn->len) {
- insn = malloc(sizeof(*insn));
- if (!insn) {
- WARN("malloc failed");
- return -1;
+ if (!insns || idx == INSN_CHUNK_MAX) {
+ insns = calloc(sizeof(*insn), INSN_CHUNK_SIZE);
+ if (!insns) {
+ WARN("malloc failed");
+ return -1;
+ }
+ idx = 0;
+ } else {
+ idx++;
}
- memset(insn, 0, sizeof(*insn));
- INIT_LIST_HEAD(&insn->alts);
- INIT_LIST_HEAD(&insn->stack_ops);
- INIT_LIST_HEAD(&insn->call_node);
+ insn = &insns[idx];
+ insn->idx = idx;
+ INIT_LIST_HEAD(&insn->call_node);
insn->sec = sec;
insn->offset = offset;
+ insn->prev_len = prev_len;
ret = arch_decode_instruction(file, sec, offset,
sec->sh.sh_size - offset,
- &insn->len, &insn->type,
- &insn->immediate,
- &insn->stack_ops);
+ insn);
if (ret)
- goto err;
+ return ret;
+
+ prev_len = insn->len;
/*
* By default, "ud2" is a dead end unless otherwise
@@ -421,10 +464,11 @@ static int decode_instructions(struct objtool_file *file)
insn->dead_end = true;
hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset));
- list_add_tail(&insn->list, &file->insn_list);
nr_insns++;
}
+// printf("%s: last chunk used: %d\n", sec->name, (int)idx);
+
list_for_each_entry(func, &sec->symbol_list, list) {
if (func->type != STT_NOTYPE && func->type != STT_FUNC)
continue;
@@ -467,10 +511,6 @@ static int decode_instructions(struct objtool_file *file)
printf("nr_insns: %lu\n", nr_insns);
return 0;
-
-err:
- free(insn);
- return ret;
}
/*
@@ -585,7 +625,7 @@ static int add_dead_ends(struct objtool_file *file)
}
insn = find_insn(file, reloc->sym->sec, reloc->addend);
if (insn)
- insn = list_prev_entry(insn, list);
+ insn = prev_insn_same_sec(file, insn);
else if (reloc->addend == reloc->sym->sec->sh.sh_size) {
insn = find_last_insn(file, reloc->sym->sec);
if (!insn) {
@@ -620,7 +660,7 @@ reachable:
}
insn = find_insn(file, reloc->sym->sec, reloc->addend);
if (insn)
- insn = list_prev_entry(insn, list);
+ insn = prev_insn_same_sec(file, insn);
else if (reloc->addend == reloc->sym->sec->sh.sh_size) {
insn = find_last_insn(file, reloc->sym->sec);
if (!insn) {
@@ -682,7 +722,7 @@ static int create_static_call_sections(struct objtool_file *file)
return -1;
/* find key symbol */
- key_name = strdup(insn->call_dest->name);
+ key_name = strdup(insn_call_dest(insn)->name);
if (!key_name) {
perror("strdup");
return -1;
@@ -690,6 +730,7 @@ static int create_static_call_sections(struct objtool_file *file)
if (strncmp(key_name, STATIC_CALL_TRAMP_PREFIX_STR,
STATIC_CALL_TRAMP_PREFIX_LEN)) {
WARN("static_call: trampoline name malformed: %s", key_name);
+ free(key_name);
return -1;
}
tmp = key_name + STATIC_CALL_TRAMP_PREFIX_LEN - STATIC_CALL_KEY_PREFIX_LEN;
@@ -699,6 +740,7 @@ static int create_static_call_sections(struct objtool_file *file)
if (!key_sym) {
if (!opts.module) {
WARN("static_call: can't find static_call_key symbol: %s", tmp);
+ free(key_name);
return -1;
}
@@ -711,7 +753,7 @@ static int create_static_call_sections(struct objtool_file *file)
* trampoline address. This is fixed up in
* static_call_add_module().
*/
- key_sym = insn->call_dest;
+ key_sym = insn_call_dest(insn);
}
free(key_name);
@@ -856,8 +898,15 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file)
list_for_each_entry(insn, &file->endbr_list, call_node) {
int *site = (int *)sec->data->d_buf + idx;
+ struct symbol *sym = insn->sym;
*site = 0;
+ if (opts.module && sym && sym->type == STT_FUNC &&
+ insn->offset == sym->offset &&
+ (!strcmp(sym->name, "init_module") ||
+ !strcmp(sym->name, "cleanup_module")))
+ WARN("%s(): not an indirect call target", sym->name);
+
if (elf_add_reloc_to_insn(file->elf, sec,
idx * sizeof(int),
R_X86_64_PC32,
@@ -1302,43 +1351,42 @@ __weak bool arch_is_rethunk(struct symbol *sym)
return false;
}
-#define NEGATIVE_RELOC ((void *)-1L)
-
static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn)
{
- if (insn->reloc == NEGATIVE_RELOC)
+ struct reloc *reloc;
+
+ if (insn->no_reloc)
return NULL;
- if (!insn->reloc) {
- if (!file)
- return NULL;
+ if (!file)
+ return NULL;
- insn->reloc = find_reloc_by_dest_range(file->elf, insn->sec,
- insn->offset, insn->len);
- if (!insn->reloc) {
- insn->reloc = NEGATIVE_RELOC;
- return NULL;
- }
+ reloc = find_reloc_by_dest_range(file->elf, insn->sec,
+ insn->offset, insn->len);
+ if (!reloc) {
+ insn->no_reloc = 1;
+ return NULL;
}
- return insn->reloc;
+ return reloc;
}
static void remove_insn_ops(struct instruction *insn)
{
- struct stack_op *op, *tmp;
+ struct stack_op *op, *next;
- list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) {
- list_del(&op->list);
+ for (op = insn->stack_ops; op; op = next) {
+ next = op->next;
free(op);
}
+ insn->stack_ops = NULL;
}
static void annotate_call_site(struct objtool_file *file,
struct instruction *insn, bool sibling)
{
struct reloc *reloc = insn_reloc(file, insn);
- struct symbol *sym = insn->call_dest;
+ struct symbol *sym = insn_call_dest(insn);
if (!sym)
sym = reloc->sym;
@@ -1423,7 +1471,7 @@ static void annotate_call_site(struct objtool_file *file,
static void add_call_dest(struct objtool_file *file, struct instruction *insn,
struct symbol *dest, bool sibling)
{
- insn->call_dest = dest;
+ insn->_call_dest = dest;
if (!dest)
return;
@@ -1681,12 +1729,12 @@ static int add_call_destinations(struct objtool_file *file)
if (insn->ignore)
continue;
- if (!insn->call_dest) {
+ if (!insn_call_dest(insn)) {
WARN_FUNC("unannotated intra-function call", insn->sec, insn->offset);
return -1;
}
- if (insn_func(insn) && insn->call_dest->type != STT_FUNC) {
+ if (insn_func(insn) && insn_call_dest(insn)->type != STT_FUNC) {
WARN_FUNC("unsupported call to non-function",
insn->sec, insn->offset);
return -1;
@@ -1724,36 +1772,50 @@ static int handle_group_alt(struct objtool_file *file,
struct instruction *orig_insn,
struct instruction **new_insn)
{
- struct instruction *last_orig_insn, *last_new_insn = NULL, *insn, *nop = NULL;
+ struct instruction *last_new_insn = NULL, *insn, *nop = NULL;
struct alt_group *orig_alt_group, *new_alt_group;
unsigned long dest_off;
-
- orig_alt_group = malloc(sizeof(*orig_alt_group));
+ orig_alt_group = orig_insn->alt_group;
if (!orig_alt_group) {
- WARN("malloc failed");
- return -1;
- }
- orig_alt_group->cfi = calloc(special_alt->orig_len,
- sizeof(struct cfi_state *));
- if (!orig_alt_group->cfi) {
- WARN("calloc failed");
- return -1;
- }
+ struct instruction *last_orig_insn = NULL;
- last_orig_insn = NULL;
- insn = orig_insn;
- sec_for_each_insn_from(file, insn) {
- if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
- break;
+ orig_alt_group = malloc(sizeof(*orig_alt_group));
+ if (!orig_alt_group) {
+ WARN("malloc failed");
+ return -1;
+ }
+ orig_alt_group->cfi = calloc(special_alt->orig_len,
+ sizeof(struct cfi_state *));
+ if (!orig_alt_group->cfi) {
+ WARN("calloc failed");
+ return -1;
+ }
- insn->alt_group = orig_alt_group;
- last_orig_insn = insn;
- }
- orig_alt_group->orig_group = NULL;
- orig_alt_group->first_insn = orig_insn;
- orig_alt_group->last_insn = last_orig_insn;
+ insn = orig_insn;
+ sec_for_each_insn_from(file, insn) {
+ if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
+ break;
+ insn->alt_group = orig_alt_group;
+ last_orig_insn = insn;
+ }
+ orig_alt_group->orig_group = NULL;
+ orig_alt_group->first_insn = orig_insn;
+ orig_alt_group->last_insn = last_orig_insn;
+ orig_alt_group->nop = NULL;
+ } else {
+ if (orig_alt_group->last_insn->offset + orig_alt_group->last_insn->len -
+ orig_alt_group->first_insn->offset != special_alt->orig_len) {
+ WARN_FUNC("weirdly overlapping alternative! %ld != %d",
+ orig_insn->sec, orig_insn->offset,
+ orig_alt_group->last_insn->offset +
+ orig_alt_group->last_insn->len -
+ orig_alt_group->first_insn->offset,
+ special_alt->orig_len);
+ return -1;
+ }
+ }
new_alt_group = malloc(sizeof(*new_alt_group));
if (!new_alt_group) {
@@ -1775,8 +1837,6 @@ static int handle_group_alt(struct objtool_file *file,
return -1;
}
memset(nop, 0, sizeof(*nop));
- INIT_LIST_HEAD(&nop->alts);
- INIT_LIST_HEAD(&nop->stack_ops);
nop->sec = special_alt->new_sec;
nop->offset = special_alt->new_off + special_alt->new_len;
@@ -1830,7 +1890,7 @@ static int handle_group_alt(struct objtool_file *file,
dest_off = arch_jump_destination(insn);
if (dest_off == special_alt->new_off + special_alt->new_len) {
- insn->jump_dest = next_insn_same_sec(file, last_orig_insn);
+ insn->jump_dest = next_insn_same_sec(file, orig_alt_group->last_insn);
if (!insn->jump_dest) {
WARN_FUNC("can't find alternative jump destination",
insn->sec, insn->offset);
@@ -1845,12 +1905,11 @@ static int handle_group_alt(struct objtool_file *file,
return -1;
}
- if (nop)
- list_add(&nop->list, &last_new_insn->list);
end:
new_alt_group->orig_group = orig_alt_group;
new_alt_group->first_insn = *new_insn;
- new_alt_group->last_insn = nop ? : last_new_insn;
+ new_alt_group->last_insn = last_new_insn;
+ new_alt_group->nop = nop;
new_alt_group->cfi = orig_alt_group->cfi;
return 0;
}
@@ -1900,7 +1959,7 @@ static int handle_jump_alt(struct objtool_file *file,
else
file->jl_long++;
- *new_insn = list_next_entry(orig_insn, list);
+ *new_insn = next_insn_same_sec(file, orig_insn);
return 0;
}
@@ -1974,7 +2033,8 @@ static int add_special_section_alts(struct objtool_file *file)
alt->insn = new_insn;
alt->skip_orig = special_alt->skip_orig;
orig_insn->ignore_alts |= special_alt->skip_alt;
- list_add_tail(&alt->list, &orig_insn->alts);
+ alt->next = orig_insn->alts;
+ orig_insn->alts = alt;
list_del(&special_alt->list);
free(special_alt);
@@ -2033,7 +2093,8 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
}
alt->insn = dest_insn;
- list_add_tail(&alt->list, &insn->alts);
+ alt->next = insn->alts;
+ insn->alts = alt;
prev_offset = reloc->offset;
}
@@ -2123,7 +2184,7 @@ static void mark_func_jump_tables(struct objtool_file *file,
reloc = find_jump_table(file, func, insn);
if (reloc) {
reloc->jump_table_start = true;
- insn->jump_table = reloc;
+ insn->_jump_table = reloc;
}
}
}
@@ -2135,10 +2196,10 @@ static int add_func_jump_tables(struct objtool_file *file,
int ret;
func_for_each_insn(file, func, insn) {
- if (!insn->jump_table)
+ if (!insn_jump_table(insn))
continue;
- ret = add_jump_table(file, insn, insn->jump_table);
+ ret = add_jump_table(file, insn, insn_jump_table(insn));
if (ret)
return ret;
}
@@ -2271,6 +2332,7 @@ static int read_unwind_hints(struct objtool_file *file)
cfi.cfa.offset = bswap_if_needed(file->elf, hint->sp_offset);
cfi.type = hint->type;
+ cfi.signal = hint->signal;
cfi.end = hint->end;
insn->cfi = cfi_hash_find_or_add(&cfi);
@@ -2610,8 +2672,8 @@ static int decode_sections(struct objtool_file *file)
static bool is_fentry_call(struct instruction *insn)
{
if (insn->type == INSN_CALL &&
- insn->call_dest &&
- insn->call_dest->fentry)
+ insn_call_dest(insn) &&
+ insn_call_dest(insn)->fentry)
return true;
return false;
@@ -3206,8 +3268,12 @@ static int propagate_alt_cfi(struct objtool_file *file, struct instruction *insn
alt_cfi[group_off] = insn->cfi;
} else {
if (cficmp(alt_cfi[group_off], insn->cfi)) {
- WARN_FUNC("stack layout conflict in alternatives",
- insn->sec, insn->offset);
+ struct alt_group *orig_group = insn->alt_group->orig_group ?: insn->alt_group;
+ struct instruction *orig = orig_group->first_insn;
+ char *where = offstr(insn->sec, insn->offset);
+ WARN_FUNC("stack layout conflict in alternatives: %s",
+ orig->sec, orig->offset, where);
+ free(where);
return -1;
}
}
@@ -3221,7 +3287,7 @@ static int handle_insn_ops(struct instruction *insn,
{
struct stack_op *op;
- list_for_each_entry(op, &insn->stack_ops, list) {
+ for (op = insn->stack_ops; op; op = op->next) {
if (update_cfi_state(insn, next_insn, &state->cfi, op))
return 1;
@@ -3318,8 +3384,8 @@ static inline const char *call_dest_name(struct instruction *insn)
struct reloc *rel;
int idx;
- if (insn->call_dest)
- return insn->call_dest->name;
+ if (insn_call_dest(insn))
+ return insn_call_dest(insn)->name;
rel = insn_reloc(NULL, insn);
if (rel && !strcmp(rel->sym->name, "pv_ops")) {
@@ -3401,13 +3467,13 @@ static int validate_call(struct objtool_file *file,
struct insn_state *state)
{
if (state->noinstr && state->instr <= 0 &&
- !noinstr_call_dest(file, insn, insn->call_dest)) {
+ !noinstr_call_dest(file, insn, insn_call_dest(insn))) {
WARN_FUNC("call to %s() leaves .noinstr.text section",
insn->sec, insn->offset, call_dest_name(insn));
return 1;
}
- if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
+ if (state->uaccess && !func_uaccess_safe(insn_call_dest(insn))) {
WARN_FUNC("call to %s() with UACCESS enabled",
insn->sec, insn->offset, call_dest_name(insn));
return 1;
@@ -3485,11 +3551,28 @@ static struct instruction *next_insn_to_validate(struct objtool_file *file,
* Simulate the fact that alternatives are patched in-place. When the
* end of a replacement alt_group is reached, redirect objtool flow to
* the end of the original alt_group.
+ *
+ * insn->alts->insn -> alt_group->first_insn
+ * ...
+ * alt_group->last_insn
+ * [alt_group->nop] -> next(orig_group->last_insn)
*/
- if (alt_group && insn == alt_group->last_insn && alt_group->orig_group)
- return next_insn_same_sec(file, alt_group->orig_group->last_insn);
+ if (alt_group) {
+ if (alt_group->nop) {
+ /* ->nop implies ->orig_group */
+ if (insn == alt_group->last_insn)
+ return alt_group->nop;
+ if (insn == alt_group->nop)
+ goto next_orig;
+ }
+ if (insn == alt_group->last_insn && alt_group->orig_group)
+ goto next_orig;
+ }
return next_insn_same_sec(file, insn);
+
+next_orig:
+ return next_insn_same_sec(file, alt_group->orig_group->last_insn);
}
/*
@@ -3590,10 +3673,10 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
if (propagate_alt_cfi(file, insn))
return 1;
- if (!insn->ignore_alts && !list_empty(&insn->alts)) {
+ if (!insn->ignore_alts && insn->alts) {
bool skip_orig = false;
- list_for_each_entry(alt, &insn->alts, list) {
+ for (alt = insn->alts; alt; alt = alt->next) {
if (alt->skip_orig)
skip_orig = true;
@@ -3740,11 +3823,25 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
return 0;
}
+static int validate_unwind_hint(struct objtool_file *file,
+ struct instruction *insn,
+ struct insn_state *state)
+{
+ if (insn->hint && !insn->visited && !insn->ignore) {
+ int ret = validate_branch(file, insn_func(insn), insn, *state);
+ if (ret && opts.backtrace)
+ BT_FUNC("<=== (hint)", insn);
+ return ret;
+ }
+
+ return 0;
+}
+
static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
{
struct instruction *insn;
struct insn_state state;
- int ret, warnings = 0;
+ int warnings = 0;
if (!file->hints)
return 0;
@@ -3752,22 +3849,11 @@ static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
init_insn_state(file, &state, sec);
if (sec) {
- insn = find_insn(file, sec, 0);
- if (!insn)
- return 0;
+ sec_for_each_insn(file, sec, insn)
+ warnings += validate_unwind_hint(file, insn, &state);
} else {
- insn = list_first_entry(&file->insn_list, typeof(*insn), list);
- }
-
- while (&insn->list != &file->insn_list && (!sec || insn->sec == sec)) {
- if (insn->hint && !insn->visited && !insn->ignore) {
- ret = validate_branch(file, insn_func(insn), insn, state);
- if (ret && opts.backtrace)
- BT_FUNC("<=== (hint)", insn);
- warnings += ret;
- }
-
- insn = list_next_entry(insn, list);
+ for_each_insn(file, insn)
+ warnings += validate_unwind_hint(file, insn, &state);
}
return warnings;
@@ -3792,11 +3878,11 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn)
insn->visited |= VISITED_ENTRY;
- if (!insn->ignore_alts && !list_empty(&insn->alts)) {
+ if (!insn->ignore_alts && insn->alts) {
struct alternative *alt;
bool skip_orig = false;
- list_for_each_entry(alt, &insn->alts, list) {
+ for (alt = insn->alts; alt; alt = alt->next) {
if (alt->skip_orig)
skip_orig = true;
@@ -3845,11 +3931,11 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn)
/* fallthrough */
case INSN_CALL:
- dest = find_insn(file, insn->call_dest->sec,
- insn->call_dest->offset);
+ dest = find_insn(file, insn_call_dest(insn)->sec,
+ insn_call_dest(insn)->offset);
if (!dest) {
WARN("Unresolved function after linking!?: %s",
- insn->call_dest->name);
+ insn_call_dest(insn)->name);
return -1;
}
@@ -3950,13 +4036,13 @@ static int validate_retpoline(struct objtool_file *file)
static bool is_kasan_insn(struct instruction *insn)
{
return (insn->type == INSN_CALL &&
- !strcmp(insn->call_dest->name, "__asan_handle_no_return"));
+ !strcmp(insn_call_dest(insn)->name, "__asan_handle_no_return"));
}
static bool is_ubsan_insn(struct instruction *insn)
{
return (insn->type == INSN_CALL &&
- !strcmp(insn->call_dest->name,
+ !strcmp(insn_call_dest(insn)->name,
"__ubsan_handle_builtin_unreachable"));
}
@@ -4033,8 +4119,9 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
*
* It may also insert a UD2 after calling a __noreturn function.
*/
- prev_insn = list_prev_entry(insn, list);
- if ((prev_insn->dead_end || dead_end_function(file, prev_insn->call_dest)) &&
+ prev_insn = prev_insn_same_sec(file, insn);
+ if ((prev_insn->dead_end ||
+ dead_end_function(file, insn_call_dest(prev_insn))) &&
(insn->type == INSN_BUG ||
(insn->type == INSN_JUMP_UNCONDITIONAL &&
insn->jump_dest && insn->jump_dest->type == INSN_BUG)))
@@ -4064,7 +4151,7 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
if (insn->offset + insn->len >= insn_func(insn)->offset + insn_func(insn)->len)
break;
- insn = list_next_entry(insn, list);
+ insn = next_insn_same_sec(file, insn);
}
return false;
@@ -4077,10 +4164,10 @@ static int add_prefix_symbol(struct objtool_file *file, struct symbol *func,
return 0;
for (;;) {
- struct instruction *prev = list_prev_entry(insn, list);
+ struct instruction *prev = prev_insn_same_sec(file, insn);
u64 offset;
- if (&prev->list == &file->insn_list)
+ if (!prev)
break;
if (prev->type != INSN_NOP)
@@ -4479,7 +4566,7 @@ int check(struct objtool_file *file)
warnings += ret;
- if (list_empty(&file->insn_list))
+ if (!nr_insns)
goto out;
if (opts.retpoline) {
@@ -4588,7 +4675,7 @@ int check(struct objtool_file *file)
warnings += ret;
}
- if (opts.orc && !list_empty(&file->insn_list)) {
+ if (opts.orc && nr_insns) {
ret = orc_create(file);
if (ret < 0)
goto out;