162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <string.h>
762306a36Sopenharmony_ci#include <stdlib.h>
862306a36Sopenharmony_ci#include <inttypes.h>
962306a36Sopenharmony_ci#include <sys/mman.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <objtool/builtin.h>
1262306a36Sopenharmony_ci#include <objtool/cfi.h>
1362306a36Sopenharmony_ci#include <objtool/arch.h>
1462306a36Sopenharmony_ci#include <objtool/check.h>
1562306a36Sopenharmony_ci#include <objtool/special.h>
1662306a36Sopenharmony_ci#include <objtool/warn.h>
1762306a36Sopenharmony_ci#include <objtool/endianness.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/objtool_types.h>
2062306a36Sopenharmony_ci#include <linux/hashtable.h>
2162306a36Sopenharmony_ci#include <linux/kernel.h>
2262306a36Sopenharmony_ci#include <linux/static_call_types.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct alternative {
2562306a36Sopenharmony_ci	struct alternative *next;
2662306a36Sopenharmony_ci	struct instruction *insn;
2762306a36Sopenharmony_ci	bool skip_orig;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic struct cfi_init_state initial_func_cfi;
3362306a36Sopenharmony_cistatic struct cfi_state init_cfi;
3462306a36Sopenharmony_cistatic struct cfi_state func_cfi;
3562306a36Sopenharmony_cistatic struct cfi_state force_undefined_cfi;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct instruction *find_insn(struct objtool_file *file,
3862306a36Sopenharmony_ci			      struct section *sec, unsigned long offset)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct instruction *insn;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, offset)) {
4362306a36Sopenharmony_ci		if (insn->sec == sec && insn->offset == offset)
4462306a36Sopenharmony_ci			return insn;
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return NULL;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistruct instruction *next_insn_same_sec(struct objtool_file *file,
5162306a36Sopenharmony_ci				       struct instruction *insn)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	if (insn->idx == INSN_CHUNK_MAX)
5462306a36Sopenharmony_ci		return find_insn(file, insn->sec, insn->offset + insn->len);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	insn++;
5762306a36Sopenharmony_ci	if (!insn->len)
5862306a36Sopenharmony_ci		return NULL;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	return insn;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic struct instruction *next_insn_same_func(struct objtool_file *file,
6462306a36Sopenharmony_ci					       struct instruction *insn)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct instruction *next = next_insn_same_sec(file, insn);
6762306a36Sopenharmony_ci	struct symbol *func = insn_func(insn);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (!func)
7062306a36Sopenharmony_ci		return NULL;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (next && insn_func(next) == func)
7362306a36Sopenharmony_ci		return next;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* Check if we're already in the subfunction: */
7662306a36Sopenharmony_ci	if (func == func->cfunc)
7762306a36Sopenharmony_ci		return NULL;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* Move to the subfunction: */
8062306a36Sopenharmony_ci	return find_insn(file, func->cfunc->sec, func->cfunc->offset);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic struct instruction *prev_insn_same_sec(struct objtool_file *file,
8462306a36Sopenharmony_ci					      struct instruction *insn)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	if (insn->idx == 0) {
8762306a36Sopenharmony_ci		if (insn->prev_len)
8862306a36Sopenharmony_ci			return find_insn(file, insn->sec, insn->offset - insn->prev_len);
8962306a36Sopenharmony_ci		return NULL;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return insn - 1;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic struct instruction *prev_insn_same_sym(struct objtool_file *file,
9662306a36Sopenharmony_ci					      struct instruction *insn)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct instruction *prev = prev_insn_same_sec(file, insn);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (prev && insn_func(prev) == insn_func(insn))
10162306a36Sopenharmony_ci		return prev;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return NULL;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci#define for_each_insn(file, insn)					\
10762306a36Sopenharmony_ci	for (struct section *__sec, *__fake = (struct section *)1;	\
10862306a36Sopenharmony_ci	     __fake; __fake = NULL)					\
10962306a36Sopenharmony_ci		for_each_sec(file, __sec)				\
11062306a36Sopenharmony_ci			sec_for_each_insn(file, __sec, insn)
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci#define func_for_each_insn(file, func, insn)				\
11362306a36Sopenharmony_ci	for (insn = find_insn(file, func->sec, func->offset);		\
11462306a36Sopenharmony_ci	     insn;							\
11562306a36Sopenharmony_ci	     insn = next_insn_same_func(file, insn))
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#define sym_for_each_insn(file, sym, insn)				\
11862306a36Sopenharmony_ci	for (insn = find_insn(file, sym->sec, sym->offset);		\
11962306a36Sopenharmony_ci	     insn && insn->offset < sym->offset + sym->len;		\
12062306a36Sopenharmony_ci	     insn = next_insn_same_sec(file, insn))
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci#define sym_for_each_insn_continue_reverse(file, sym, insn)		\
12362306a36Sopenharmony_ci	for (insn = prev_insn_same_sec(file, insn);			\
12462306a36Sopenharmony_ci	     insn && insn->offset >= sym->offset;			\
12562306a36Sopenharmony_ci	     insn = prev_insn_same_sec(file, insn))
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci#define sec_for_each_insn_from(file, insn)				\
12862306a36Sopenharmony_ci	for (; insn; insn = next_insn_same_sec(file, insn))
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#define sec_for_each_insn_continue(file, insn)				\
13162306a36Sopenharmony_ci	for (insn = next_insn_same_sec(file, insn); insn;		\
13262306a36Sopenharmony_ci	     insn = next_insn_same_sec(file, insn))
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic inline struct symbol *insn_call_dest(struct instruction *insn)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	if (insn->type == INSN_JUMP_DYNAMIC ||
13762306a36Sopenharmony_ci	    insn->type == INSN_CALL_DYNAMIC)
13862306a36Sopenharmony_ci		return NULL;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return insn->_call_dest;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic inline struct reloc *insn_jump_table(struct instruction *insn)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	if (insn->type == INSN_JUMP_DYNAMIC ||
14662306a36Sopenharmony_ci	    insn->type == INSN_CALL_DYNAMIC)
14762306a36Sopenharmony_ci		return insn->_jump_table;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return NULL;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic bool is_jump_table_jump(struct instruction *insn)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct alt_group *alt_group = insn->alt_group;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (insn_jump_table(insn))
15762306a36Sopenharmony_ci		return true;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/* Retpoline alternative for a jump table? */
16062306a36Sopenharmony_ci	return alt_group && alt_group->orig_group &&
16162306a36Sopenharmony_ci	       insn_jump_table(alt_group->orig_group->first_insn);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic bool is_sibling_call(struct instruction *insn)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	/*
16762306a36Sopenharmony_ci	 * Assume only STT_FUNC calls have jump-tables.
16862306a36Sopenharmony_ci	 */
16962306a36Sopenharmony_ci	if (insn_func(insn)) {
17062306a36Sopenharmony_ci		/* An indirect jump is either a sibling call or a jump to a table. */
17162306a36Sopenharmony_ci		if (insn->type == INSN_JUMP_DYNAMIC)
17262306a36Sopenharmony_ci			return !is_jump_table_jump(insn);
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* add_jump_destinations() sets insn_call_dest(insn) for sibling calls. */
17662306a36Sopenharmony_ci	return (is_static_jump(insn) && insn_call_dest(insn));
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/*
18062306a36Sopenharmony_ci * This checks to see if the given function is a "noreturn" function.
18162306a36Sopenharmony_ci *
18262306a36Sopenharmony_ci * For global functions which are outside the scope of this object file, we
18362306a36Sopenharmony_ci * have to keep a manual list of them.
18462306a36Sopenharmony_ci *
18562306a36Sopenharmony_ci * For local functions, we have to detect them manually by simply looking for
18662306a36Sopenharmony_ci * the lack of a return instruction.
18762306a36Sopenharmony_ci */
18862306a36Sopenharmony_cistatic bool __dead_end_function(struct objtool_file *file, struct symbol *func,
18962306a36Sopenharmony_ci				int recursion)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	int i;
19262306a36Sopenharmony_ci	struct instruction *insn;
19362306a36Sopenharmony_ci	bool empty = true;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci#define NORETURN(func) __stringify(func),
19662306a36Sopenharmony_ci	static const char * const global_noreturns[] = {
19762306a36Sopenharmony_ci#include "noreturns.h"
19862306a36Sopenharmony_ci	};
19962306a36Sopenharmony_ci#undef NORETURN
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (!func)
20262306a36Sopenharmony_ci		return false;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (func->bind == STB_GLOBAL || func->bind == STB_WEAK)
20562306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
20662306a36Sopenharmony_ci			if (!strcmp(func->name, global_noreturns[i]))
20762306a36Sopenharmony_ci				return true;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (func->bind == STB_WEAK)
21062306a36Sopenharmony_ci		return false;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (!func->len)
21362306a36Sopenharmony_ci		return false;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	insn = find_insn(file, func->sec, func->offset);
21662306a36Sopenharmony_ci	if (!insn || !insn_func(insn))
21762306a36Sopenharmony_ci		return false;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	func_for_each_insn(file, func, insn) {
22062306a36Sopenharmony_ci		empty = false;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		if (insn->type == INSN_RETURN)
22362306a36Sopenharmony_ci			return false;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (empty)
22762306a36Sopenharmony_ci		return false;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/*
23062306a36Sopenharmony_ci	 * A function can have a sibling call instead of a return.  In that
23162306a36Sopenharmony_ci	 * case, the function's dead-end status depends on whether the target
23262306a36Sopenharmony_ci	 * of the sibling call returns.
23362306a36Sopenharmony_ci	 */
23462306a36Sopenharmony_ci	func_for_each_insn(file, func, insn) {
23562306a36Sopenharmony_ci		if (is_sibling_call(insn)) {
23662306a36Sopenharmony_ci			struct instruction *dest = insn->jump_dest;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci			if (!dest)
23962306a36Sopenharmony_ci				/* sibling call to another file */
24062306a36Sopenharmony_ci				return false;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci			/* local sibling call */
24362306a36Sopenharmony_ci			if (recursion == 5) {
24462306a36Sopenharmony_ci				/*
24562306a36Sopenharmony_ci				 * Infinite recursion: two functions have
24662306a36Sopenharmony_ci				 * sibling calls to each other.  This is a very
24762306a36Sopenharmony_ci				 * rare case.  It means they aren't dead ends.
24862306a36Sopenharmony_ci				 */
24962306a36Sopenharmony_ci				return false;
25062306a36Sopenharmony_ci			}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci			return __dead_end_function(file, insn_func(dest), recursion+1);
25362306a36Sopenharmony_ci		}
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return true;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic bool dead_end_function(struct objtool_file *file, struct symbol *func)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	return __dead_end_function(file, func, 0);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void init_cfi_state(struct cfi_state *cfi)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	int i;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	for (i = 0; i < CFI_NUM_REGS; i++) {
26962306a36Sopenharmony_ci		cfi->regs[i].base = CFI_UNDEFINED;
27062306a36Sopenharmony_ci		cfi->vals[i].base = CFI_UNDEFINED;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci	cfi->cfa.base = CFI_UNDEFINED;
27362306a36Sopenharmony_ci	cfi->drap_reg = CFI_UNDEFINED;
27462306a36Sopenharmony_ci	cfi->drap_offset = -1;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic void init_insn_state(struct objtool_file *file, struct insn_state *state,
27862306a36Sopenharmony_ci			    struct section *sec)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	memset(state, 0, sizeof(*state));
28162306a36Sopenharmony_ci	init_cfi_state(&state->cfi);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	/*
28462306a36Sopenharmony_ci	 * We need the full vmlinux for noinstr validation, otherwise we can
28562306a36Sopenharmony_ci	 * not correctly determine insn_call_dest(insn)->sec (external symbols
28662306a36Sopenharmony_ci	 * do not have a section).
28762306a36Sopenharmony_ci	 */
28862306a36Sopenharmony_ci	if (opts.link && opts.noinstr && sec)
28962306a36Sopenharmony_ci		state->noinstr = sec->noinstr;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic struct cfi_state *cfi_alloc(void)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct cfi_state *cfi = calloc(sizeof(struct cfi_state), 1);
29562306a36Sopenharmony_ci	if (!cfi) {
29662306a36Sopenharmony_ci		WARN("calloc failed");
29762306a36Sopenharmony_ci		exit(1);
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci	nr_cfi++;
30062306a36Sopenharmony_ci	return cfi;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic int cfi_bits;
30462306a36Sopenharmony_cistatic struct hlist_head *cfi_hash;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic inline bool cficmp(struct cfi_state *cfi1, struct cfi_state *cfi2)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	return memcmp((void *)cfi1 + sizeof(cfi1->hash),
30962306a36Sopenharmony_ci		      (void *)cfi2 + sizeof(cfi2->hash),
31062306a36Sopenharmony_ci		      sizeof(struct cfi_state) - sizeof(struct hlist_node));
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic inline u32 cfi_key(struct cfi_state *cfi)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	return jhash((void *)cfi + sizeof(cfi->hash),
31662306a36Sopenharmony_ci		     sizeof(*cfi) - sizeof(cfi->hash), 0);
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic struct cfi_state *cfi_hash_find_or_add(struct cfi_state *cfi)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct hlist_head *head = &cfi_hash[hash_min(cfi_key(cfi), cfi_bits)];
32262306a36Sopenharmony_ci	struct cfi_state *obj;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	hlist_for_each_entry(obj, head, hash) {
32562306a36Sopenharmony_ci		if (!cficmp(cfi, obj)) {
32662306a36Sopenharmony_ci			nr_cfi_cache++;
32762306a36Sopenharmony_ci			return obj;
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	obj = cfi_alloc();
33262306a36Sopenharmony_ci	*obj = *cfi;
33362306a36Sopenharmony_ci	hlist_add_head(&obj->hash, head);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	return obj;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic void cfi_hash_add(struct cfi_state *cfi)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	struct hlist_head *head = &cfi_hash[hash_min(cfi_key(cfi), cfi_bits)];
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	hlist_add_head(&cfi->hash, head);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic void *cfi_hash_alloc(unsigned long size)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	cfi_bits = max(10, ilog2(size));
34862306a36Sopenharmony_ci	cfi_hash = mmap(NULL, sizeof(struct hlist_head) << cfi_bits,
34962306a36Sopenharmony_ci			PROT_READ|PROT_WRITE,
35062306a36Sopenharmony_ci			MAP_PRIVATE|MAP_ANON, -1, 0);
35162306a36Sopenharmony_ci	if (cfi_hash == (void *)-1L) {
35262306a36Sopenharmony_ci		WARN("mmap fail cfi_hash");
35362306a36Sopenharmony_ci		cfi_hash = NULL;
35462306a36Sopenharmony_ci	}  else if (opts.stats) {
35562306a36Sopenharmony_ci		printf("cfi_bits: %d\n", cfi_bits);
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	return cfi_hash;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic unsigned long nr_insns;
36262306a36Sopenharmony_cistatic unsigned long nr_insns_visited;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci/*
36562306a36Sopenharmony_ci * Call the arch-specific instruction decoder for all the instructions and add
36662306a36Sopenharmony_ci * them to the global instruction list.
36762306a36Sopenharmony_ci */
36862306a36Sopenharmony_cistatic int decode_instructions(struct objtool_file *file)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	struct section *sec;
37162306a36Sopenharmony_ci	struct symbol *func;
37262306a36Sopenharmony_ci	unsigned long offset;
37362306a36Sopenharmony_ci	struct instruction *insn;
37462306a36Sopenharmony_ci	int ret;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	for_each_sec(file, sec) {
37762306a36Sopenharmony_ci		struct instruction *insns = NULL;
37862306a36Sopenharmony_ci		u8 prev_len = 0;
37962306a36Sopenharmony_ci		u8 idx = 0;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		if (!(sec->sh.sh_flags & SHF_EXECINSTR))
38262306a36Sopenharmony_ci			continue;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		if (strcmp(sec->name, ".altinstr_replacement") &&
38562306a36Sopenharmony_ci		    strcmp(sec->name, ".altinstr_aux") &&
38662306a36Sopenharmony_ci		    strncmp(sec->name, ".discard.", 9))
38762306a36Sopenharmony_ci			sec->text = true;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		if (!strcmp(sec->name, ".noinstr.text") ||
39062306a36Sopenharmony_ci		    !strcmp(sec->name, ".entry.text") ||
39162306a36Sopenharmony_ci		    !strcmp(sec->name, ".cpuidle.text") ||
39262306a36Sopenharmony_ci		    !strncmp(sec->name, ".text..__x86.", 13))
39362306a36Sopenharmony_ci			sec->noinstr = true;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci		/*
39662306a36Sopenharmony_ci		 * .init.text code is ran before userspace and thus doesn't
39762306a36Sopenharmony_ci		 * strictly need retpolines, except for modules which are
39862306a36Sopenharmony_ci		 * loaded late, they very much do need retpoline in their
39962306a36Sopenharmony_ci		 * .init.text
40062306a36Sopenharmony_ci		 */
40162306a36Sopenharmony_ci		if (!strcmp(sec->name, ".init.text") && !opts.module)
40262306a36Sopenharmony_ci			sec->init = true;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		for (offset = 0; offset < sec->sh.sh_size; offset += insn->len) {
40562306a36Sopenharmony_ci			if (!insns || idx == INSN_CHUNK_MAX) {
40662306a36Sopenharmony_ci				insns = calloc(sizeof(*insn), INSN_CHUNK_SIZE);
40762306a36Sopenharmony_ci				if (!insns) {
40862306a36Sopenharmony_ci					WARN("malloc failed");
40962306a36Sopenharmony_ci					return -1;
41062306a36Sopenharmony_ci				}
41162306a36Sopenharmony_ci				idx = 0;
41262306a36Sopenharmony_ci			} else {
41362306a36Sopenharmony_ci				idx++;
41462306a36Sopenharmony_ci			}
41562306a36Sopenharmony_ci			insn = &insns[idx];
41662306a36Sopenharmony_ci			insn->idx = idx;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci			INIT_LIST_HEAD(&insn->call_node);
41962306a36Sopenharmony_ci			insn->sec = sec;
42062306a36Sopenharmony_ci			insn->offset = offset;
42162306a36Sopenharmony_ci			insn->prev_len = prev_len;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci			ret = arch_decode_instruction(file, sec, offset,
42462306a36Sopenharmony_ci						      sec->sh.sh_size - offset,
42562306a36Sopenharmony_ci						      insn);
42662306a36Sopenharmony_ci			if (ret)
42762306a36Sopenharmony_ci				return ret;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci			prev_len = insn->len;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci			/*
43262306a36Sopenharmony_ci			 * By default, "ud2" is a dead end unless otherwise
43362306a36Sopenharmony_ci			 * annotated, because GCC 7 inserts it for certain
43462306a36Sopenharmony_ci			 * divide-by-zero cases.
43562306a36Sopenharmony_ci			 */
43662306a36Sopenharmony_ci			if (insn->type == INSN_BUG)
43762306a36Sopenharmony_ci				insn->dead_end = true;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci			hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset));
44062306a36Sopenharmony_ci			nr_insns++;
44162306a36Sopenharmony_ci		}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci//		printf("%s: last chunk used: %d\n", sec->name, (int)idx);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		sec_for_each_sym(sec, func) {
44662306a36Sopenharmony_ci			if (func->type != STT_NOTYPE && func->type != STT_FUNC)
44762306a36Sopenharmony_ci				continue;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci			if (func->offset == sec->sh.sh_size) {
45062306a36Sopenharmony_ci				/* Heuristic: likely an "end" symbol */
45162306a36Sopenharmony_ci				if (func->type == STT_NOTYPE)
45262306a36Sopenharmony_ci					continue;
45362306a36Sopenharmony_ci				WARN("%s(): STT_FUNC at end of section",
45462306a36Sopenharmony_ci				     func->name);
45562306a36Sopenharmony_ci				return -1;
45662306a36Sopenharmony_ci			}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci			if (func->embedded_insn || func->alias != func)
45962306a36Sopenharmony_ci				continue;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci			if (!find_insn(file, sec, func->offset)) {
46262306a36Sopenharmony_ci				WARN("%s(): can't find starting instruction",
46362306a36Sopenharmony_ci				     func->name);
46462306a36Sopenharmony_ci				return -1;
46562306a36Sopenharmony_ci			}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci			sym_for_each_insn(file, func, insn) {
46862306a36Sopenharmony_ci				insn->sym = func;
46962306a36Sopenharmony_ci				if (func->type == STT_FUNC &&
47062306a36Sopenharmony_ci				    insn->type == INSN_ENDBR &&
47162306a36Sopenharmony_ci				    list_empty(&insn->call_node)) {
47262306a36Sopenharmony_ci					if (insn->offset == func->offset) {
47362306a36Sopenharmony_ci						list_add_tail(&insn->call_node, &file->endbr_list);
47462306a36Sopenharmony_ci						file->nr_endbr++;
47562306a36Sopenharmony_ci					} else {
47662306a36Sopenharmony_ci						file->nr_endbr_int++;
47762306a36Sopenharmony_ci					}
47862306a36Sopenharmony_ci				}
47962306a36Sopenharmony_ci			}
48062306a36Sopenharmony_ci		}
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (opts.stats)
48462306a36Sopenharmony_ci		printf("nr_insns: %lu\n", nr_insns);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	return 0;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci/*
49062306a36Sopenharmony_ci * Read the pv_ops[] .data table to find the static initialized values.
49162306a36Sopenharmony_ci */
49262306a36Sopenharmony_cistatic int add_pv_ops(struct objtool_file *file, const char *symname)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	struct symbol *sym, *func;
49562306a36Sopenharmony_ci	unsigned long off, end;
49662306a36Sopenharmony_ci	struct reloc *reloc;
49762306a36Sopenharmony_ci	int idx;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	sym = find_symbol_by_name(file->elf, symname);
50062306a36Sopenharmony_ci	if (!sym)
50162306a36Sopenharmony_ci		return 0;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	off = sym->offset;
50462306a36Sopenharmony_ci	end = off + sym->len;
50562306a36Sopenharmony_ci	for (;;) {
50662306a36Sopenharmony_ci		reloc = find_reloc_by_dest_range(file->elf, sym->sec, off, end - off);
50762306a36Sopenharmony_ci		if (!reloc)
50862306a36Sopenharmony_ci			break;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci		func = reloc->sym;
51162306a36Sopenharmony_ci		if (func->type == STT_SECTION)
51262306a36Sopenharmony_ci			func = find_symbol_by_offset(reloc->sym->sec,
51362306a36Sopenharmony_ci						     reloc_addend(reloc));
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		idx = (reloc_offset(reloc) - sym->offset) / sizeof(unsigned long);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		objtool_pv_add(file, idx, func);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		off = reloc_offset(reloc) + 1;
52062306a36Sopenharmony_ci		if (off > end)
52162306a36Sopenharmony_ci			break;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	return 0;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci/*
52862306a36Sopenharmony_ci * Allocate and initialize file->pv_ops[].
52962306a36Sopenharmony_ci */
53062306a36Sopenharmony_cistatic int init_pv_ops(struct objtool_file *file)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	static const char *pv_ops_tables[] = {
53362306a36Sopenharmony_ci		"pv_ops",
53462306a36Sopenharmony_ci		"xen_cpu_ops",
53562306a36Sopenharmony_ci		"xen_irq_ops",
53662306a36Sopenharmony_ci		"xen_mmu_ops",
53762306a36Sopenharmony_ci		NULL,
53862306a36Sopenharmony_ci	};
53962306a36Sopenharmony_ci	const char *pv_ops;
54062306a36Sopenharmony_ci	struct symbol *sym;
54162306a36Sopenharmony_ci	int idx, nr;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (!opts.noinstr)
54462306a36Sopenharmony_ci		return 0;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	file->pv_ops = NULL;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	sym = find_symbol_by_name(file->elf, "pv_ops");
54962306a36Sopenharmony_ci	if (!sym)
55062306a36Sopenharmony_ci		return 0;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	nr = sym->len / sizeof(unsigned long);
55362306a36Sopenharmony_ci	file->pv_ops = calloc(sizeof(struct pv_state), nr);
55462306a36Sopenharmony_ci	if (!file->pv_ops)
55562306a36Sopenharmony_ci		return -1;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	for (idx = 0; idx < nr; idx++)
55862306a36Sopenharmony_ci		INIT_LIST_HEAD(&file->pv_ops[idx].targets);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	for (idx = 0; (pv_ops = pv_ops_tables[idx]); idx++)
56162306a36Sopenharmony_ci		add_pv_ops(file, pv_ops);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	return 0;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic struct instruction *find_last_insn(struct objtool_file *file,
56762306a36Sopenharmony_ci					  struct section *sec)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	struct instruction *insn = NULL;
57062306a36Sopenharmony_ci	unsigned int offset;
57162306a36Sopenharmony_ci	unsigned int end = (sec->sh.sh_size > 10) ? sec->sh.sh_size - 10 : 0;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	for (offset = sec->sh.sh_size - 1; offset >= end && !insn; offset--)
57462306a36Sopenharmony_ci		insn = find_insn(file, sec, offset);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	return insn;
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci/*
58062306a36Sopenharmony_ci * Mark "ud2" instructions and manually annotated dead ends.
58162306a36Sopenharmony_ci */
58262306a36Sopenharmony_cistatic int add_dead_ends(struct objtool_file *file)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	struct section *rsec;
58562306a36Sopenharmony_ci	struct reloc *reloc;
58662306a36Sopenharmony_ci	struct instruction *insn;
58762306a36Sopenharmony_ci	s64 addend;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/*
59062306a36Sopenharmony_ci	 * Check for manually annotated dead ends.
59162306a36Sopenharmony_ci	 */
59262306a36Sopenharmony_ci	rsec = find_section_by_name(file->elf, ".rela.discard.unreachable");
59362306a36Sopenharmony_ci	if (!rsec)
59462306a36Sopenharmony_ci		goto reachable;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	for_each_reloc(rsec, reloc) {
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		if (reloc->sym->type != STT_SECTION) {
59962306a36Sopenharmony_ci			WARN("unexpected relocation symbol type in %s", rsec->name);
60062306a36Sopenharmony_ci			return -1;
60162306a36Sopenharmony_ci		}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci		addend = reloc_addend(reloc);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci		insn = find_insn(file, reloc->sym->sec, addend);
60662306a36Sopenharmony_ci		if (insn)
60762306a36Sopenharmony_ci			insn = prev_insn_same_sec(file, insn);
60862306a36Sopenharmony_ci		else if (addend == reloc->sym->sec->sh.sh_size) {
60962306a36Sopenharmony_ci			insn = find_last_insn(file, reloc->sym->sec);
61062306a36Sopenharmony_ci			if (!insn) {
61162306a36Sopenharmony_ci				WARN("can't find unreachable insn at %s+0x%" PRIx64,
61262306a36Sopenharmony_ci				     reloc->sym->sec->name, addend);
61362306a36Sopenharmony_ci				return -1;
61462306a36Sopenharmony_ci			}
61562306a36Sopenharmony_ci		} else {
61662306a36Sopenharmony_ci			WARN("can't find unreachable insn at %s+0x%" PRIx64,
61762306a36Sopenharmony_ci			     reloc->sym->sec->name, addend);
61862306a36Sopenharmony_ci			return -1;
61962306a36Sopenharmony_ci		}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci		insn->dead_end = true;
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cireachable:
62562306a36Sopenharmony_ci	/*
62662306a36Sopenharmony_ci	 * These manually annotated reachable checks are needed for GCC 4.4,
62762306a36Sopenharmony_ci	 * where the Linux unreachable() macro isn't supported.  In that case
62862306a36Sopenharmony_ci	 * GCC doesn't know the "ud2" is fatal, so it generates code as if it's
62962306a36Sopenharmony_ci	 * not a dead end.
63062306a36Sopenharmony_ci	 */
63162306a36Sopenharmony_ci	rsec = find_section_by_name(file->elf, ".rela.discard.reachable");
63262306a36Sopenharmony_ci	if (!rsec)
63362306a36Sopenharmony_ci		return 0;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	for_each_reloc(rsec, reloc) {
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci		if (reloc->sym->type != STT_SECTION) {
63862306a36Sopenharmony_ci			WARN("unexpected relocation symbol type in %s", rsec->name);
63962306a36Sopenharmony_ci			return -1;
64062306a36Sopenharmony_ci		}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci		addend = reloc_addend(reloc);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		insn = find_insn(file, reloc->sym->sec, addend);
64562306a36Sopenharmony_ci		if (insn)
64662306a36Sopenharmony_ci			insn = prev_insn_same_sec(file, insn);
64762306a36Sopenharmony_ci		else if (addend == reloc->sym->sec->sh.sh_size) {
64862306a36Sopenharmony_ci			insn = find_last_insn(file, reloc->sym->sec);
64962306a36Sopenharmony_ci			if (!insn) {
65062306a36Sopenharmony_ci				WARN("can't find reachable insn at %s+0x%" PRIx64,
65162306a36Sopenharmony_ci				     reloc->sym->sec->name, addend);
65262306a36Sopenharmony_ci				return -1;
65362306a36Sopenharmony_ci			}
65462306a36Sopenharmony_ci		} else {
65562306a36Sopenharmony_ci			WARN("can't find reachable insn at %s+0x%" PRIx64,
65662306a36Sopenharmony_ci			     reloc->sym->sec->name, addend);
65762306a36Sopenharmony_ci			return -1;
65862306a36Sopenharmony_ci		}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		insn->dead_end = false;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	return 0;
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic int create_static_call_sections(struct objtool_file *file)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	struct static_call_site *site;
66962306a36Sopenharmony_ci	struct section *sec;
67062306a36Sopenharmony_ci	struct instruction *insn;
67162306a36Sopenharmony_ci	struct symbol *key_sym;
67262306a36Sopenharmony_ci	char *key_name, *tmp;
67362306a36Sopenharmony_ci	int idx;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	sec = find_section_by_name(file->elf, ".static_call_sites");
67662306a36Sopenharmony_ci	if (sec) {
67762306a36Sopenharmony_ci		INIT_LIST_HEAD(&file->static_call_list);
67862306a36Sopenharmony_ci		WARN("file already has .static_call_sites section, skipping");
67962306a36Sopenharmony_ci		return 0;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	if (list_empty(&file->static_call_list))
68362306a36Sopenharmony_ci		return 0;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	idx = 0;
68662306a36Sopenharmony_ci	list_for_each_entry(insn, &file->static_call_list, call_node)
68762306a36Sopenharmony_ci		idx++;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	sec = elf_create_section_pair(file->elf, ".static_call_sites",
69062306a36Sopenharmony_ci				      sizeof(*site), idx, idx * 2);
69162306a36Sopenharmony_ci	if (!sec)
69262306a36Sopenharmony_ci		return -1;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	/* Allow modules to modify the low bits of static_call_site::key */
69562306a36Sopenharmony_ci	sec->sh.sh_flags |= SHF_WRITE;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	idx = 0;
69862306a36Sopenharmony_ci	list_for_each_entry(insn, &file->static_call_list, call_node) {
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci		/* populate reloc for 'addr' */
70162306a36Sopenharmony_ci		if (!elf_init_reloc_text_sym(file->elf, sec,
70262306a36Sopenharmony_ci					     idx * sizeof(*site), idx * 2,
70362306a36Sopenharmony_ci					     insn->sec, insn->offset))
70462306a36Sopenharmony_ci			return -1;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci		/* find key symbol */
70762306a36Sopenharmony_ci		key_name = strdup(insn_call_dest(insn)->name);
70862306a36Sopenharmony_ci		if (!key_name) {
70962306a36Sopenharmony_ci			perror("strdup");
71062306a36Sopenharmony_ci			return -1;
71162306a36Sopenharmony_ci		}
71262306a36Sopenharmony_ci		if (strncmp(key_name, STATIC_CALL_TRAMP_PREFIX_STR,
71362306a36Sopenharmony_ci			    STATIC_CALL_TRAMP_PREFIX_LEN)) {
71462306a36Sopenharmony_ci			WARN("static_call: trampoline name malformed: %s", key_name);
71562306a36Sopenharmony_ci			free(key_name);
71662306a36Sopenharmony_ci			return -1;
71762306a36Sopenharmony_ci		}
71862306a36Sopenharmony_ci		tmp = key_name + STATIC_CALL_TRAMP_PREFIX_LEN - STATIC_CALL_KEY_PREFIX_LEN;
71962306a36Sopenharmony_ci		memcpy(tmp, STATIC_CALL_KEY_PREFIX_STR, STATIC_CALL_KEY_PREFIX_LEN);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci		key_sym = find_symbol_by_name(file->elf, tmp);
72262306a36Sopenharmony_ci		if (!key_sym) {
72362306a36Sopenharmony_ci			if (!opts.module) {
72462306a36Sopenharmony_ci				WARN("static_call: can't find static_call_key symbol: %s", tmp);
72562306a36Sopenharmony_ci				free(key_name);
72662306a36Sopenharmony_ci				return -1;
72762306a36Sopenharmony_ci			}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci			/*
73062306a36Sopenharmony_ci			 * For modules(), the key might not be exported, which
73162306a36Sopenharmony_ci			 * means the module can make static calls but isn't
73262306a36Sopenharmony_ci			 * allowed to change them.
73362306a36Sopenharmony_ci			 *
73462306a36Sopenharmony_ci			 * In that case we temporarily set the key to be the
73562306a36Sopenharmony_ci			 * trampoline address.  This is fixed up in
73662306a36Sopenharmony_ci			 * static_call_add_module().
73762306a36Sopenharmony_ci			 */
73862306a36Sopenharmony_ci			key_sym = insn_call_dest(insn);
73962306a36Sopenharmony_ci		}
74062306a36Sopenharmony_ci		free(key_name);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		/* populate reloc for 'key' */
74362306a36Sopenharmony_ci		if (!elf_init_reloc_data_sym(file->elf, sec,
74462306a36Sopenharmony_ci					     idx * sizeof(*site) + 4,
74562306a36Sopenharmony_ci					     (idx * 2) + 1, key_sym,
74662306a36Sopenharmony_ci					     is_sibling_call(insn) * STATIC_CALL_SITE_TAIL))
74762306a36Sopenharmony_ci			return -1;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci		idx++;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	return 0;
75362306a36Sopenharmony_ci}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_cistatic int create_retpoline_sites_sections(struct objtool_file *file)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	struct instruction *insn;
75862306a36Sopenharmony_ci	struct section *sec;
75962306a36Sopenharmony_ci	int idx;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	sec = find_section_by_name(file->elf, ".retpoline_sites");
76262306a36Sopenharmony_ci	if (sec) {
76362306a36Sopenharmony_ci		WARN("file already has .retpoline_sites, skipping");
76462306a36Sopenharmony_ci		return 0;
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	idx = 0;
76862306a36Sopenharmony_ci	list_for_each_entry(insn, &file->retpoline_call_list, call_node)
76962306a36Sopenharmony_ci		idx++;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (!idx)
77262306a36Sopenharmony_ci		return 0;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	sec = elf_create_section_pair(file->elf, ".retpoline_sites",
77562306a36Sopenharmony_ci				      sizeof(int), idx, idx);
77662306a36Sopenharmony_ci	if (!sec)
77762306a36Sopenharmony_ci		return -1;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	idx = 0;
78062306a36Sopenharmony_ci	list_for_each_entry(insn, &file->retpoline_call_list, call_node) {
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci		if (!elf_init_reloc_text_sym(file->elf, sec,
78362306a36Sopenharmony_ci					     idx * sizeof(int), idx,
78462306a36Sopenharmony_ci					     insn->sec, insn->offset))
78562306a36Sopenharmony_ci			return -1;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci		idx++;
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	return 0;
79162306a36Sopenharmony_ci}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_cistatic int create_return_sites_sections(struct objtool_file *file)
79462306a36Sopenharmony_ci{
79562306a36Sopenharmony_ci	struct instruction *insn;
79662306a36Sopenharmony_ci	struct section *sec;
79762306a36Sopenharmony_ci	int idx;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	sec = find_section_by_name(file->elf, ".return_sites");
80062306a36Sopenharmony_ci	if (sec) {
80162306a36Sopenharmony_ci		WARN("file already has .return_sites, skipping");
80262306a36Sopenharmony_ci		return 0;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	idx = 0;
80662306a36Sopenharmony_ci	list_for_each_entry(insn, &file->return_thunk_list, call_node)
80762306a36Sopenharmony_ci		idx++;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (!idx)
81062306a36Sopenharmony_ci		return 0;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	sec = elf_create_section_pair(file->elf, ".return_sites",
81362306a36Sopenharmony_ci				      sizeof(int), idx, idx);
81462306a36Sopenharmony_ci	if (!sec)
81562306a36Sopenharmony_ci		return -1;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	idx = 0;
81862306a36Sopenharmony_ci	list_for_each_entry(insn, &file->return_thunk_list, call_node) {
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		if (!elf_init_reloc_text_sym(file->elf, sec,
82162306a36Sopenharmony_ci					     idx * sizeof(int), idx,
82262306a36Sopenharmony_ci					     insn->sec, insn->offset))
82362306a36Sopenharmony_ci			return -1;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci		idx++;
82662306a36Sopenharmony_ci	}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	return 0;
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_cistatic int create_ibt_endbr_seal_sections(struct objtool_file *file)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	struct instruction *insn;
83462306a36Sopenharmony_ci	struct section *sec;
83562306a36Sopenharmony_ci	int idx;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	sec = find_section_by_name(file->elf, ".ibt_endbr_seal");
83862306a36Sopenharmony_ci	if (sec) {
83962306a36Sopenharmony_ci		WARN("file already has .ibt_endbr_seal, skipping");
84062306a36Sopenharmony_ci		return 0;
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	idx = 0;
84462306a36Sopenharmony_ci	list_for_each_entry(insn, &file->endbr_list, call_node)
84562306a36Sopenharmony_ci		idx++;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	if (opts.stats) {
84862306a36Sopenharmony_ci		printf("ibt: ENDBR at function start: %d\n", file->nr_endbr);
84962306a36Sopenharmony_ci		printf("ibt: ENDBR inside functions:  %d\n", file->nr_endbr_int);
85062306a36Sopenharmony_ci		printf("ibt: superfluous ENDBR:       %d\n", idx);
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (!idx)
85462306a36Sopenharmony_ci		return 0;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	sec = elf_create_section_pair(file->elf, ".ibt_endbr_seal",
85762306a36Sopenharmony_ci				      sizeof(int), idx, idx);
85862306a36Sopenharmony_ci	if (!sec)
85962306a36Sopenharmony_ci		return -1;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	idx = 0;
86262306a36Sopenharmony_ci	list_for_each_entry(insn, &file->endbr_list, call_node) {
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci		int *site = (int *)sec->data->d_buf + idx;
86562306a36Sopenharmony_ci		struct symbol *sym = insn->sym;
86662306a36Sopenharmony_ci		*site = 0;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci		if (opts.module && sym && sym->type == STT_FUNC &&
86962306a36Sopenharmony_ci		    insn->offset == sym->offset &&
87062306a36Sopenharmony_ci		    (!strcmp(sym->name, "init_module") ||
87162306a36Sopenharmony_ci		     !strcmp(sym->name, "cleanup_module")))
87262306a36Sopenharmony_ci			WARN("%s(): not an indirect call target", sym->name);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci		if (!elf_init_reloc_text_sym(file->elf, sec,
87562306a36Sopenharmony_ci					     idx * sizeof(int), idx,
87662306a36Sopenharmony_ci					     insn->sec, insn->offset))
87762306a36Sopenharmony_ci			return -1;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci		idx++;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	return 0;
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_cistatic int create_cfi_sections(struct objtool_file *file)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	struct section *sec;
88862306a36Sopenharmony_ci	struct symbol *sym;
88962306a36Sopenharmony_ci	int idx;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	sec = find_section_by_name(file->elf, ".cfi_sites");
89262306a36Sopenharmony_ci	if (sec) {
89362306a36Sopenharmony_ci		INIT_LIST_HEAD(&file->call_list);
89462306a36Sopenharmony_ci		WARN("file already has .cfi_sites section, skipping");
89562306a36Sopenharmony_ci		return 0;
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	idx = 0;
89962306a36Sopenharmony_ci	for_each_sym(file, sym) {
90062306a36Sopenharmony_ci		if (sym->type != STT_FUNC)
90162306a36Sopenharmony_ci			continue;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci		if (strncmp(sym->name, "__cfi_", 6))
90462306a36Sopenharmony_ci			continue;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci		idx++;
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	sec = elf_create_section_pair(file->elf, ".cfi_sites",
91062306a36Sopenharmony_ci				      sizeof(unsigned int), idx, idx);
91162306a36Sopenharmony_ci	if (!sec)
91262306a36Sopenharmony_ci		return -1;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	idx = 0;
91562306a36Sopenharmony_ci	for_each_sym(file, sym) {
91662306a36Sopenharmony_ci		if (sym->type != STT_FUNC)
91762306a36Sopenharmony_ci			continue;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci		if (strncmp(sym->name, "__cfi_", 6))
92062306a36Sopenharmony_ci			continue;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci		if (!elf_init_reloc_text_sym(file->elf, sec,
92362306a36Sopenharmony_ci					     idx * sizeof(unsigned int), idx,
92462306a36Sopenharmony_ci					     sym->sec, sym->offset))
92562306a36Sopenharmony_ci			return -1;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci		idx++;
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	return 0;
93162306a36Sopenharmony_ci}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_cistatic int create_mcount_loc_sections(struct objtool_file *file)
93462306a36Sopenharmony_ci{
93562306a36Sopenharmony_ci	size_t addr_size = elf_addr_size(file->elf);
93662306a36Sopenharmony_ci	struct instruction *insn;
93762306a36Sopenharmony_ci	struct section *sec;
93862306a36Sopenharmony_ci	int idx;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	sec = find_section_by_name(file->elf, "__mcount_loc");
94162306a36Sopenharmony_ci	if (sec) {
94262306a36Sopenharmony_ci		INIT_LIST_HEAD(&file->mcount_loc_list);
94362306a36Sopenharmony_ci		WARN("file already has __mcount_loc section, skipping");
94462306a36Sopenharmony_ci		return 0;
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	if (list_empty(&file->mcount_loc_list))
94862306a36Sopenharmony_ci		return 0;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	idx = 0;
95162306a36Sopenharmony_ci	list_for_each_entry(insn, &file->mcount_loc_list, call_node)
95262306a36Sopenharmony_ci		idx++;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	sec = elf_create_section_pair(file->elf, "__mcount_loc", addr_size,
95562306a36Sopenharmony_ci				      idx, idx);
95662306a36Sopenharmony_ci	if (!sec)
95762306a36Sopenharmony_ci		return -1;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	sec->sh.sh_addralign = addr_size;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	idx = 0;
96262306a36Sopenharmony_ci	list_for_each_entry(insn, &file->mcount_loc_list, call_node) {
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci		struct reloc *reloc;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci		reloc = elf_init_reloc_text_sym(file->elf, sec, idx * addr_size, idx,
96762306a36Sopenharmony_ci					       insn->sec, insn->offset);
96862306a36Sopenharmony_ci		if (!reloc)
96962306a36Sopenharmony_ci			return -1;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci		set_reloc_type(file->elf, reloc, addr_size == 8 ? R_ABS64 : R_ABS32);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci		idx++;
97462306a36Sopenharmony_ci	}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	return 0;
97762306a36Sopenharmony_ci}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_cistatic int create_direct_call_sections(struct objtool_file *file)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	struct instruction *insn;
98262306a36Sopenharmony_ci	struct section *sec;
98362306a36Sopenharmony_ci	int idx;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	sec = find_section_by_name(file->elf, ".call_sites");
98662306a36Sopenharmony_ci	if (sec) {
98762306a36Sopenharmony_ci		INIT_LIST_HEAD(&file->call_list);
98862306a36Sopenharmony_ci		WARN("file already has .call_sites section, skipping");
98962306a36Sopenharmony_ci		return 0;
99062306a36Sopenharmony_ci	}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	if (list_empty(&file->call_list))
99362306a36Sopenharmony_ci		return 0;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	idx = 0;
99662306a36Sopenharmony_ci	list_for_each_entry(insn, &file->call_list, call_node)
99762306a36Sopenharmony_ci		idx++;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	sec = elf_create_section_pair(file->elf, ".call_sites",
100062306a36Sopenharmony_ci				      sizeof(unsigned int), idx, idx);
100162306a36Sopenharmony_ci	if (!sec)
100262306a36Sopenharmony_ci		return -1;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	idx = 0;
100562306a36Sopenharmony_ci	list_for_each_entry(insn, &file->call_list, call_node) {
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci		if (!elf_init_reloc_text_sym(file->elf, sec,
100862306a36Sopenharmony_ci					     idx * sizeof(unsigned int), idx,
100962306a36Sopenharmony_ci					     insn->sec, insn->offset))
101062306a36Sopenharmony_ci			return -1;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci		idx++;
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	return 0;
101662306a36Sopenharmony_ci}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci/*
101962306a36Sopenharmony_ci * Warnings shouldn't be reported for ignored functions.
102062306a36Sopenharmony_ci */
102162306a36Sopenharmony_cistatic void add_ignores(struct objtool_file *file)
102262306a36Sopenharmony_ci{
102362306a36Sopenharmony_ci	struct instruction *insn;
102462306a36Sopenharmony_ci	struct section *rsec;
102562306a36Sopenharmony_ci	struct symbol *func;
102662306a36Sopenharmony_ci	struct reloc *reloc;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	rsec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard");
102962306a36Sopenharmony_ci	if (!rsec)
103062306a36Sopenharmony_ci		return;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	for_each_reloc(rsec, reloc) {
103362306a36Sopenharmony_ci		switch (reloc->sym->type) {
103462306a36Sopenharmony_ci		case STT_FUNC:
103562306a36Sopenharmony_ci			func = reloc->sym;
103662306a36Sopenharmony_ci			break;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci		case STT_SECTION:
103962306a36Sopenharmony_ci			func = find_func_by_offset(reloc->sym->sec, reloc_addend(reloc));
104062306a36Sopenharmony_ci			if (!func)
104162306a36Sopenharmony_ci				continue;
104262306a36Sopenharmony_ci			break;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci		default:
104562306a36Sopenharmony_ci			WARN("unexpected relocation symbol type in %s: %d",
104662306a36Sopenharmony_ci			     rsec->name, reloc->sym->type);
104762306a36Sopenharmony_ci			continue;
104862306a36Sopenharmony_ci		}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci		func_for_each_insn(file, func, insn)
105162306a36Sopenharmony_ci			insn->ignore = true;
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci/*
105662306a36Sopenharmony_ci * This is a whitelist of functions that is allowed to be called with AC set.
105762306a36Sopenharmony_ci * The list is meant to be minimal and only contains compiler instrumentation
105862306a36Sopenharmony_ci * ABI and a few functions used to implement *_{to,from}_user() functions.
105962306a36Sopenharmony_ci *
106062306a36Sopenharmony_ci * These functions must not directly change AC, but may PUSHF/POPF.
106162306a36Sopenharmony_ci */
106262306a36Sopenharmony_cistatic const char *uaccess_safe_builtin[] = {
106362306a36Sopenharmony_ci	/* KASAN */
106462306a36Sopenharmony_ci	"kasan_report",
106562306a36Sopenharmony_ci	"kasan_check_range",
106662306a36Sopenharmony_ci	/* KASAN out-of-line */
106762306a36Sopenharmony_ci	"__asan_loadN_noabort",
106862306a36Sopenharmony_ci	"__asan_load1_noabort",
106962306a36Sopenharmony_ci	"__asan_load2_noabort",
107062306a36Sopenharmony_ci	"__asan_load4_noabort",
107162306a36Sopenharmony_ci	"__asan_load8_noabort",
107262306a36Sopenharmony_ci	"__asan_load16_noabort",
107362306a36Sopenharmony_ci	"__asan_storeN_noabort",
107462306a36Sopenharmony_ci	"__asan_store1_noabort",
107562306a36Sopenharmony_ci	"__asan_store2_noabort",
107662306a36Sopenharmony_ci	"__asan_store4_noabort",
107762306a36Sopenharmony_ci	"__asan_store8_noabort",
107862306a36Sopenharmony_ci	"__asan_store16_noabort",
107962306a36Sopenharmony_ci	"__kasan_check_read",
108062306a36Sopenharmony_ci	"__kasan_check_write",
108162306a36Sopenharmony_ci	/* KASAN in-line */
108262306a36Sopenharmony_ci	"__asan_report_load_n_noabort",
108362306a36Sopenharmony_ci	"__asan_report_load1_noabort",
108462306a36Sopenharmony_ci	"__asan_report_load2_noabort",
108562306a36Sopenharmony_ci	"__asan_report_load4_noabort",
108662306a36Sopenharmony_ci	"__asan_report_load8_noabort",
108762306a36Sopenharmony_ci	"__asan_report_load16_noabort",
108862306a36Sopenharmony_ci	"__asan_report_store_n_noabort",
108962306a36Sopenharmony_ci	"__asan_report_store1_noabort",
109062306a36Sopenharmony_ci	"__asan_report_store2_noabort",
109162306a36Sopenharmony_ci	"__asan_report_store4_noabort",
109262306a36Sopenharmony_ci	"__asan_report_store8_noabort",
109362306a36Sopenharmony_ci	"__asan_report_store16_noabort",
109462306a36Sopenharmony_ci	/* KCSAN */
109562306a36Sopenharmony_ci	"__kcsan_check_access",
109662306a36Sopenharmony_ci	"__kcsan_mb",
109762306a36Sopenharmony_ci	"__kcsan_wmb",
109862306a36Sopenharmony_ci	"__kcsan_rmb",
109962306a36Sopenharmony_ci	"__kcsan_release",
110062306a36Sopenharmony_ci	"kcsan_found_watchpoint",
110162306a36Sopenharmony_ci	"kcsan_setup_watchpoint",
110262306a36Sopenharmony_ci	"kcsan_check_scoped_accesses",
110362306a36Sopenharmony_ci	"kcsan_disable_current",
110462306a36Sopenharmony_ci	"kcsan_enable_current_nowarn",
110562306a36Sopenharmony_ci	/* KCSAN/TSAN */
110662306a36Sopenharmony_ci	"__tsan_func_entry",
110762306a36Sopenharmony_ci	"__tsan_func_exit",
110862306a36Sopenharmony_ci	"__tsan_read_range",
110962306a36Sopenharmony_ci	"__tsan_write_range",
111062306a36Sopenharmony_ci	"__tsan_read1",
111162306a36Sopenharmony_ci	"__tsan_read2",
111262306a36Sopenharmony_ci	"__tsan_read4",
111362306a36Sopenharmony_ci	"__tsan_read8",
111462306a36Sopenharmony_ci	"__tsan_read16",
111562306a36Sopenharmony_ci	"__tsan_write1",
111662306a36Sopenharmony_ci	"__tsan_write2",
111762306a36Sopenharmony_ci	"__tsan_write4",
111862306a36Sopenharmony_ci	"__tsan_write8",
111962306a36Sopenharmony_ci	"__tsan_write16",
112062306a36Sopenharmony_ci	"__tsan_read_write1",
112162306a36Sopenharmony_ci	"__tsan_read_write2",
112262306a36Sopenharmony_ci	"__tsan_read_write4",
112362306a36Sopenharmony_ci	"__tsan_read_write8",
112462306a36Sopenharmony_ci	"__tsan_read_write16",
112562306a36Sopenharmony_ci	"__tsan_volatile_read1",
112662306a36Sopenharmony_ci	"__tsan_volatile_read2",
112762306a36Sopenharmony_ci	"__tsan_volatile_read4",
112862306a36Sopenharmony_ci	"__tsan_volatile_read8",
112962306a36Sopenharmony_ci	"__tsan_volatile_read16",
113062306a36Sopenharmony_ci	"__tsan_volatile_write1",
113162306a36Sopenharmony_ci	"__tsan_volatile_write2",
113262306a36Sopenharmony_ci	"__tsan_volatile_write4",
113362306a36Sopenharmony_ci	"__tsan_volatile_write8",
113462306a36Sopenharmony_ci	"__tsan_volatile_write16",
113562306a36Sopenharmony_ci	"__tsan_atomic8_load",
113662306a36Sopenharmony_ci	"__tsan_atomic16_load",
113762306a36Sopenharmony_ci	"__tsan_atomic32_load",
113862306a36Sopenharmony_ci	"__tsan_atomic64_load",
113962306a36Sopenharmony_ci	"__tsan_atomic8_store",
114062306a36Sopenharmony_ci	"__tsan_atomic16_store",
114162306a36Sopenharmony_ci	"__tsan_atomic32_store",
114262306a36Sopenharmony_ci	"__tsan_atomic64_store",
114362306a36Sopenharmony_ci	"__tsan_atomic8_exchange",
114462306a36Sopenharmony_ci	"__tsan_atomic16_exchange",
114562306a36Sopenharmony_ci	"__tsan_atomic32_exchange",
114662306a36Sopenharmony_ci	"__tsan_atomic64_exchange",
114762306a36Sopenharmony_ci	"__tsan_atomic8_fetch_add",
114862306a36Sopenharmony_ci	"__tsan_atomic16_fetch_add",
114962306a36Sopenharmony_ci	"__tsan_atomic32_fetch_add",
115062306a36Sopenharmony_ci	"__tsan_atomic64_fetch_add",
115162306a36Sopenharmony_ci	"__tsan_atomic8_fetch_sub",
115262306a36Sopenharmony_ci	"__tsan_atomic16_fetch_sub",
115362306a36Sopenharmony_ci	"__tsan_atomic32_fetch_sub",
115462306a36Sopenharmony_ci	"__tsan_atomic64_fetch_sub",
115562306a36Sopenharmony_ci	"__tsan_atomic8_fetch_and",
115662306a36Sopenharmony_ci	"__tsan_atomic16_fetch_and",
115762306a36Sopenharmony_ci	"__tsan_atomic32_fetch_and",
115862306a36Sopenharmony_ci	"__tsan_atomic64_fetch_and",
115962306a36Sopenharmony_ci	"__tsan_atomic8_fetch_or",
116062306a36Sopenharmony_ci	"__tsan_atomic16_fetch_or",
116162306a36Sopenharmony_ci	"__tsan_atomic32_fetch_or",
116262306a36Sopenharmony_ci	"__tsan_atomic64_fetch_or",
116362306a36Sopenharmony_ci	"__tsan_atomic8_fetch_xor",
116462306a36Sopenharmony_ci	"__tsan_atomic16_fetch_xor",
116562306a36Sopenharmony_ci	"__tsan_atomic32_fetch_xor",
116662306a36Sopenharmony_ci	"__tsan_atomic64_fetch_xor",
116762306a36Sopenharmony_ci	"__tsan_atomic8_fetch_nand",
116862306a36Sopenharmony_ci	"__tsan_atomic16_fetch_nand",
116962306a36Sopenharmony_ci	"__tsan_atomic32_fetch_nand",
117062306a36Sopenharmony_ci	"__tsan_atomic64_fetch_nand",
117162306a36Sopenharmony_ci	"__tsan_atomic8_compare_exchange_strong",
117262306a36Sopenharmony_ci	"__tsan_atomic16_compare_exchange_strong",
117362306a36Sopenharmony_ci	"__tsan_atomic32_compare_exchange_strong",
117462306a36Sopenharmony_ci	"__tsan_atomic64_compare_exchange_strong",
117562306a36Sopenharmony_ci	"__tsan_atomic8_compare_exchange_weak",
117662306a36Sopenharmony_ci	"__tsan_atomic16_compare_exchange_weak",
117762306a36Sopenharmony_ci	"__tsan_atomic32_compare_exchange_weak",
117862306a36Sopenharmony_ci	"__tsan_atomic64_compare_exchange_weak",
117962306a36Sopenharmony_ci	"__tsan_atomic8_compare_exchange_val",
118062306a36Sopenharmony_ci	"__tsan_atomic16_compare_exchange_val",
118162306a36Sopenharmony_ci	"__tsan_atomic32_compare_exchange_val",
118262306a36Sopenharmony_ci	"__tsan_atomic64_compare_exchange_val",
118362306a36Sopenharmony_ci	"__tsan_atomic_thread_fence",
118462306a36Sopenharmony_ci	"__tsan_atomic_signal_fence",
118562306a36Sopenharmony_ci	"__tsan_unaligned_read16",
118662306a36Sopenharmony_ci	"__tsan_unaligned_write16",
118762306a36Sopenharmony_ci	/* KCOV */
118862306a36Sopenharmony_ci	"write_comp_data",
118962306a36Sopenharmony_ci	"check_kcov_mode",
119062306a36Sopenharmony_ci	"__sanitizer_cov_trace_pc",
119162306a36Sopenharmony_ci	"__sanitizer_cov_trace_const_cmp1",
119262306a36Sopenharmony_ci	"__sanitizer_cov_trace_const_cmp2",
119362306a36Sopenharmony_ci	"__sanitizer_cov_trace_const_cmp4",
119462306a36Sopenharmony_ci	"__sanitizer_cov_trace_const_cmp8",
119562306a36Sopenharmony_ci	"__sanitizer_cov_trace_cmp1",
119662306a36Sopenharmony_ci	"__sanitizer_cov_trace_cmp2",
119762306a36Sopenharmony_ci	"__sanitizer_cov_trace_cmp4",
119862306a36Sopenharmony_ci	"__sanitizer_cov_trace_cmp8",
119962306a36Sopenharmony_ci	"__sanitizer_cov_trace_switch",
120062306a36Sopenharmony_ci	/* KMSAN */
120162306a36Sopenharmony_ci	"kmsan_copy_to_user",
120262306a36Sopenharmony_ci	"kmsan_report",
120362306a36Sopenharmony_ci	"kmsan_unpoison_entry_regs",
120462306a36Sopenharmony_ci	"kmsan_unpoison_memory",
120562306a36Sopenharmony_ci	"__msan_chain_origin",
120662306a36Sopenharmony_ci	"__msan_get_context_state",
120762306a36Sopenharmony_ci	"__msan_instrument_asm_store",
120862306a36Sopenharmony_ci	"__msan_metadata_ptr_for_load_1",
120962306a36Sopenharmony_ci	"__msan_metadata_ptr_for_load_2",
121062306a36Sopenharmony_ci	"__msan_metadata_ptr_for_load_4",
121162306a36Sopenharmony_ci	"__msan_metadata_ptr_for_load_8",
121262306a36Sopenharmony_ci	"__msan_metadata_ptr_for_load_n",
121362306a36Sopenharmony_ci	"__msan_metadata_ptr_for_store_1",
121462306a36Sopenharmony_ci	"__msan_metadata_ptr_for_store_2",
121562306a36Sopenharmony_ci	"__msan_metadata_ptr_for_store_4",
121662306a36Sopenharmony_ci	"__msan_metadata_ptr_for_store_8",
121762306a36Sopenharmony_ci	"__msan_metadata_ptr_for_store_n",
121862306a36Sopenharmony_ci	"__msan_poison_alloca",
121962306a36Sopenharmony_ci	"__msan_warning",
122062306a36Sopenharmony_ci	/* UBSAN */
122162306a36Sopenharmony_ci	"ubsan_type_mismatch_common",
122262306a36Sopenharmony_ci	"__ubsan_handle_type_mismatch",
122362306a36Sopenharmony_ci	"__ubsan_handle_type_mismatch_v1",
122462306a36Sopenharmony_ci	"__ubsan_handle_shift_out_of_bounds",
122562306a36Sopenharmony_ci	"__ubsan_handle_load_invalid_value",
122662306a36Sopenharmony_ci	/* STACKLEAK */
122762306a36Sopenharmony_ci	"stackleak_track_stack",
122862306a36Sopenharmony_ci	/* misc */
122962306a36Sopenharmony_ci	"csum_partial_copy_generic",
123062306a36Sopenharmony_ci	"copy_mc_fragile",
123162306a36Sopenharmony_ci	"copy_mc_fragile_handle_tail",
123262306a36Sopenharmony_ci	"copy_mc_enhanced_fast_string",
123362306a36Sopenharmony_ci	"ftrace_likely_update", /* CONFIG_TRACE_BRANCH_PROFILING */
123462306a36Sopenharmony_ci	"rep_stos_alternative",
123562306a36Sopenharmony_ci	"rep_movs_alternative",
123662306a36Sopenharmony_ci	"__copy_user_nocache",
123762306a36Sopenharmony_ci	NULL
123862306a36Sopenharmony_ci};
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic void add_uaccess_safe(struct objtool_file *file)
124162306a36Sopenharmony_ci{
124262306a36Sopenharmony_ci	struct symbol *func;
124362306a36Sopenharmony_ci	const char **name;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	if (!opts.uaccess)
124662306a36Sopenharmony_ci		return;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	for (name = uaccess_safe_builtin; *name; name++) {
124962306a36Sopenharmony_ci		func = find_symbol_by_name(file->elf, *name);
125062306a36Sopenharmony_ci		if (!func)
125162306a36Sopenharmony_ci			continue;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci		func->uaccess_safe = true;
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci/*
125862306a36Sopenharmony_ci * FIXME: For now, just ignore any alternatives which add retpolines.  This is
125962306a36Sopenharmony_ci * a temporary hack, as it doesn't allow ORC to unwind from inside a retpoline.
126062306a36Sopenharmony_ci * But it at least allows objtool to understand the control flow *around* the
126162306a36Sopenharmony_ci * retpoline.
126262306a36Sopenharmony_ci */
126362306a36Sopenharmony_cistatic int add_ignore_alternatives(struct objtool_file *file)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	struct section *rsec;
126662306a36Sopenharmony_ci	struct reloc *reloc;
126762306a36Sopenharmony_ci	struct instruction *insn;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	rsec = find_section_by_name(file->elf, ".rela.discard.ignore_alts");
127062306a36Sopenharmony_ci	if (!rsec)
127162306a36Sopenharmony_ci		return 0;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	for_each_reloc(rsec, reloc) {
127462306a36Sopenharmony_ci		if (reloc->sym->type != STT_SECTION) {
127562306a36Sopenharmony_ci			WARN("unexpected relocation symbol type in %s", rsec->name);
127662306a36Sopenharmony_ci			return -1;
127762306a36Sopenharmony_ci		}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci		insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
128062306a36Sopenharmony_ci		if (!insn) {
128162306a36Sopenharmony_ci			WARN("bad .discard.ignore_alts entry");
128262306a36Sopenharmony_ci			return -1;
128362306a36Sopenharmony_ci		}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci		insn->ignore_alts = true;
128662306a36Sopenharmony_ci	}
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	return 0;
128962306a36Sopenharmony_ci}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci/*
129262306a36Sopenharmony_ci * Symbols that replace INSN_CALL_DYNAMIC, every (tail) call to such a symbol
129362306a36Sopenharmony_ci * will be added to the .retpoline_sites section.
129462306a36Sopenharmony_ci */
129562306a36Sopenharmony_ci__weak bool arch_is_retpoline(struct symbol *sym)
129662306a36Sopenharmony_ci{
129762306a36Sopenharmony_ci	return false;
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci/*
130162306a36Sopenharmony_ci * Symbols that replace INSN_RETURN, every (tail) call to such a symbol
130262306a36Sopenharmony_ci * will be added to the .return_sites section.
130362306a36Sopenharmony_ci */
130462306a36Sopenharmony_ci__weak bool arch_is_rethunk(struct symbol *sym)
130562306a36Sopenharmony_ci{
130662306a36Sopenharmony_ci	return false;
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci/*
131062306a36Sopenharmony_ci * Symbols that are embedded inside other instructions, because sometimes crazy
131162306a36Sopenharmony_ci * code exists. These are mostly ignored for validation purposes.
131262306a36Sopenharmony_ci */
131362306a36Sopenharmony_ci__weak bool arch_is_embedded_insn(struct symbol *sym)
131462306a36Sopenharmony_ci{
131562306a36Sopenharmony_ci	return false;
131662306a36Sopenharmony_ci}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_cistatic struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn)
131962306a36Sopenharmony_ci{
132062306a36Sopenharmony_ci	struct reloc *reloc;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	if (insn->no_reloc)
132362306a36Sopenharmony_ci		return NULL;
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	if (!file)
132662306a36Sopenharmony_ci		return NULL;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	reloc = find_reloc_by_dest_range(file->elf, insn->sec,
132962306a36Sopenharmony_ci					 insn->offset, insn->len);
133062306a36Sopenharmony_ci	if (!reloc) {
133162306a36Sopenharmony_ci		insn->no_reloc = 1;
133262306a36Sopenharmony_ci		return NULL;
133362306a36Sopenharmony_ci	}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	return reloc;
133662306a36Sopenharmony_ci}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_cistatic void remove_insn_ops(struct instruction *insn)
133962306a36Sopenharmony_ci{
134062306a36Sopenharmony_ci	struct stack_op *op, *next;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	for (op = insn->stack_ops; op; op = next) {
134362306a36Sopenharmony_ci		next = op->next;
134462306a36Sopenharmony_ci		free(op);
134562306a36Sopenharmony_ci	}
134662306a36Sopenharmony_ci	insn->stack_ops = NULL;
134762306a36Sopenharmony_ci}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_cistatic void annotate_call_site(struct objtool_file *file,
135062306a36Sopenharmony_ci			       struct instruction *insn, bool sibling)
135162306a36Sopenharmony_ci{
135262306a36Sopenharmony_ci	struct reloc *reloc = insn_reloc(file, insn);
135362306a36Sopenharmony_ci	struct symbol *sym = insn_call_dest(insn);
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	if (!sym)
135662306a36Sopenharmony_ci		sym = reloc->sym;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	/*
135962306a36Sopenharmony_ci	 * Alternative replacement code is just template code which is
136062306a36Sopenharmony_ci	 * sometimes copied to the original instruction. For now, don't
136162306a36Sopenharmony_ci	 * annotate it. (In the future we might consider annotating the
136262306a36Sopenharmony_ci	 * original instruction if/when it ever makes sense to do so.)
136362306a36Sopenharmony_ci	 */
136462306a36Sopenharmony_ci	if (!strcmp(insn->sec->name, ".altinstr_replacement"))
136562306a36Sopenharmony_ci		return;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	if (sym->static_call_tramp) {
136862306a36Sopenharmony_ci		list_add_tail(&insn->call_node, &file->static_call_list);
136962306a36Sopenharmony_ci		return;
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	if (sym->retpoline_thunk) {
137362306a36Sopenharmony_ci		list_add_tail(&insn->call_node, &file->retpoline_call_list);
137462306a36Sopenharmony_ci		return;
137562306a36Sopenharmony_ci	}
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	/*
137862306a36Sopenharmony_ci	 * Many compilers cannot disable KCOV or sanitizer calls with a function
137962306a36Sopenharmony_ci	 * attribute so they need a little help, NOP out any such calls from
138062306a36Sopenharmony_ci	 * noinstr text.
138162306a36Sopenharmony_ci	 */
138262306a36Sopenharmony_ci	if (opts.hack_noinstr && insn->sec->noinstr && sym->profiling_func) {
138362306a36Sopenharmony_ci		if (reloc)
138462306a36Sopenharmony_ci			set_reloc_type(file->elf, reloc, R_NONE);
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci		elf_write_insn(file->elf, insn->sec,
138762306a36Sopenharmony_ci			       insn->offset, insn->len,
138862306a36Sopenharmony_ci			       sibling ? arch_ret_insn(insn->len)
138962306a36Sopenharmony_ci			               : arch_nop_insn(insn->len));
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci		insn->type = sibling ? INSN_RETURN : INSN_NOP;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci		if (sibling) {
139462306a36Sopenharmony_ci			/*
139562306a36Sopenharmony_ci			 * We've replaced the tail-call JMP insn by two new
139662306a36Sopenharmony_ci			 * insn: RET; INT3, except we only have a single struct
139762306a36Sopenharmony_ci			 * insn here. Mark it retpoline_safe to avoid the SLS
139862306a36Sopenharmony_ci			 * warning, instead of adding another insn.
139962306a36Sopenharmony_ci			 */
140062306a36Sopenharmony_ci			insn->retpoline_safe = true;
140162306a36Sopenharmony_ci		}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci		return;
140462306a36Sopenharmony_ci	}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	if (opts.mcount && sym->fentry) {
140762306a36Sopenharmony_ci		if (sibling)
140862306a36Sopenharmony_ci			WARN_INSN(insn, "tail call to __fentry__ !?!?");
140962306a36Sopenharmony_ci		if (opts.mnop) {
141062306a36Sopenharmony_ci			if (reloc)
141162306a36Sopenharmony_ci				set_reloc_type(file->elf, reloc, R_NONE);
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci			elf_write_insn(file->elf, insn->sec,
141462306a36Sopenharmony_ci				       insn->offset, insn->len,
141562306a36Sopenharmony_ci				       arch_nop_insn(insn->len));
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci			insn->type = INSN_NOP;
141862306a36Sopenharmony_ci		}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci		list_add_tail(&insn->call_node, &file->mcount_loc_list);
142162306a36Sopenharmony_ci		return;
142262306a36Sopenharmony_ci	}
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	if (insn->type == INSN_CALL && !insn->sec->init)
142562306a36Sopenharmony_ci		list_add_tail(&insn->call_node, &file->call_list);
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	if (!sibling && dead_end_function(file, sym))
142862306a36Sopenharmony_ci		insn->dead_end = true;
142962306a36Sopenharmony_ci}
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_cistatic void add_call_dest(struct objtool_file *file, struct instruction *insn,
143262306a36Sopenharmony_ci			  struct symbol *dest, bool sibling)
143362306a36Sopenharmony_ci{
143462306a36Sopenharmony_ci	insn->_call_dest = dest;
143562306a36Sopenharmony_ci	if (!dest)
143662306a36Sopenharmony_ci		return;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	/*
143962306a36Sopenharmony_ci	 * Whatever stack impact regular CALLs have, should be undone
144062306a36Sopenharmony_ci	 * by the RETURN of the called function.
144162306a36Sopenharmony_ci	 *
144262306a36Sopenharmony_ci	 * Annotated intra-function calls retain the stack_ops but
144362306a36Sopenharmony_ci	 * are converted to JUMP, see read_intra_function_calls().
144462306a36Sopenharmony_ci	 */
144562306a36Sopenharmony_ci	remove_insn_ops(insn);
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	annotate_call_site(file, insn, sibling);
144862306a36Sopenharmony_ci}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_cistatic void add_retpoline_call(struct objtool_file *file, struct instruction *insn)
145162306a36Sopenharmony_ci{
145262306a36Sopenharmony_ci	/*
145362306a36Sopenharmony_ci	 * Retpoline calls/jumps are really dynamic calls/jumps in disguise,
145462306a36Sopenharmony_ci	 * so convert them accordingly.
145562306a36Sopenharmony_ci	 */
145662306a36Sopenharmony_ci	switch (insn->type) {
145762306a36Sopenharmony_ci	case INSN_CALL:
145862306a36Sopenharmony_ci		insn->type = INSN_CALL_DYNAMIC;
145962306a36Sopenharmony_ci		break;
146062306a36Sopenharmony_ci	case INSN_JUMP_UNCONDITIONAL:
146162306a36Sopenharmony_ci		insn->type = INSN_JUMP_DYNAMIC;
146262306a36Sopenharmony_ci		break;
146362306a36Sopenharmony_ci	case INSN_JUMP_CONDITIONAL:
146462306a36Sopenharmony_ci		insn->type = INSN_JUMP_DYNAMIC_CONDITIONAL;
146562306a36Sopenharmony_ci		break;
146662306a36Sopenharmony_ci	default:
146762306a36Sopenharmony_ci		return;
146862306a36Sopenharmony_ci	}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	insn->retpoline_safe = true;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	/*
147362306a36Sopenharmony_ci	 * Whatever stack impact regular CALLs have, should be undone
147462306a36Sopenharmony_ci	 * by the RETURN of the called function.
147562306a36Sopenharmony_ci	 *
147662306a36Sopenharmony_ci	 * Annotated intra-function calls retain the stack_ops but
147762306a36Sopenharmony_ci	 * are converted to JUMP, see read_intra_function_calls().
147862306a36Sopenharmony_ci	 */
147962306a36Sopenharmony_ci	remove_insn_ops(insn);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	annotate_call_site(file, insn, false);
148262306a36Sopenharmony_ci}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_cistatic void add_return_call(struct objtool_file *file, struct instruction *insn, bool add)
148562306a36Sopenharmony_ci{
148662306a36Sopenharmony_ci	/*
148762306a36Sopenharmony_ci	 * Return thunk tail calls are really just returns in disguise,
148862306a36Sopenharmony_ci	 * so convert them accordingly.
148962306a36Sopenharmony_ci	 */
149062306a36Sopenharmony_ci	insn->type = INSN_RETURN;
149162306a36Sopenharmony_ci	insn->retpoline_safe = true;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	if (add)
149462306a36Sopenharmony_ci		list_add_tail(&insn->call_node, &file->return_thunk_list);
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_cistatic bool is_first_func_insn(struct objtool_file *file,
149862306a36Sopenharmony_ci			       struct instruction *insn, struct symbol *sym)
149962306a36Sopenharmony_ci{
150062306a36Sopenharmony_ci	if (insn->offset == sym->offset)
150162306a36Sopenharmony_ci		return true;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	/* Allow direct CALL/JMP past ENDBR */
150462306a36Sopenharmony_ci	if (opts.ibt) {
150562306a36Sopenharmony_ci		struct instruction *prev = prev_insn_same_sym(file, insn);
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci		if (prev && prev->type == INSN_ENDBR &&
150862306a36Sopenharmony_ci		    insn->offset == sym->offset + prev->len)
150962306a36Sopenharmony_ci			return true;
151062306a36Sopenharmony_ci	}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	return false;
151362306a36Sopenharmony_ci}
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci/*
151662306a36Sopenharmony_ci * A sibling call is a tail-call to another symbol -- to differentiate from a
151762306a36Sopenharmony_ci * recursive tail-call which is to the same symbol.
151862306a36Sopenharmony_ci */
151962306a36Sopenharmony_cistatic bool jump_is_sibling_call(struct objtool_file *file,
152062306a36Sopenharmony_ci				 struct instruction *from, struct instruction *to)
152162306a36Sopenharmony_ci{
152262306a36Sopenharmony_ci	struct symbol *fs = from->sym;
152362306a36Sopenharmony_ci	struct symbol *ts = to->sym;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	/* Not a sibling call if from/to a symbol hole */
152662306a36Sopenharmony_ci	if (!fs || !ts)
152762306a36Sopenharmony_ci		return false;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	/* Not a sibling call if not targeting the start of a symbol. */
153062306a36Sopenharmony_ci	if (!is_first_func_insn(file, to, ts))
153162306a36Sopenharmony_ci		return false;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	/* Disallow sibling calls into STT_NOTYPE */
153462306a36Sopenharmony_ci	if (ts->type == STT_NOTYPE)
153562306a36Sopenharmony_ci		return false;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	/* Must not be self to be a sibling */
153862306a36Sopenharmony_ci	return fs->pfunc != ts->pfunc;
153962306a36Sopenharmony_ci}
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci/*
154262306a36Sopenharmony_ci * Find the destination instructions for all jumps.
154362306a36Sopenharmony_ci */
154462306a36Sopenharmony_cistatic int add_jump_destinations(struct objtool_file *file)
154562306a36Sopenharmony_ci{
154662306a36Sopenharmony_ci	struct instruction *insn, *jump_dest;
154762306a36Sopenharmony_ci	struct reloc *reloc;
154862306a36Sopenharmony_ci	struct section *dest_sec;
154962306a36Sopenharmony_ci	unsigned long dest_off;
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	for_each_insn(file, insn) {
155262306a36Sopenharmony_ci		if (insn->jump_dest) {
155362306a36Sopenharmony_ci			/*
155462306a36Sopenharmony_ci			 * handle_group_alt() may have previously set
155562306a36Sopenharmony_ci			 * 'jump_dest' for some alternatives.
155662306a36Sopenharmony_ci			 */
155762306a36Sopenharmony_ci			continue;
155862306a36Sopenharmony_ci		}
155962306a36Sopenharmony_ci		if (!is_static_jump(insn))
156062306a36Sopenharmony_ci			continue;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci		reloc = insn_reloc(file, insn);
156362306a36Sopenharmony_ci		if (!reloc) {
156462306a36Sopenharmony_ci			dest_sec = insn->sec;
156562306a36Sopenharmony_ci			dest_off = arch_jump_destination(insn);
156662306a36Sopenharmony_ci		} else if (reloc->sym->type == STT_SECTION) {
156762306a36Sopenharmony_ci			dest_sec = reloc->sym->sec;
156862306a36Sopenharmony_ci			dest_off = arch_dest_reloc_offset(reloc_addend(reloc));
156962306a36Sopenharmony_ci		} else if (reloc->sym->retpoline_thunk) {
157062306a36Sopenharmony_ci			add_retpoline_call(file, insn);
157162306a36Sopenharmony_ci			continue;
157262306a36Sopenharmony_ci		} else if (reloc->sym->return_thunk) {
157362306a36Sopenharmony_ci			add_return_call(file, insn, true);
157462306a36Sopenharmony_ci			continue;
157562306a36Sopenharmony_ci		} else if (insn_func(insn)) {
157662306a36Sopenharmony_ci			/*
157762306a36Sopenharmony_ci			 * External sibling call or internal sibling call with
157862306a36Sopenharmony_ci			 * STT_FUNC reloc.
157962306a36Sopenharmony_ci			 */
158062306a36Sopenharmony_ci			add_call_dest(file, insn, reloc->sym, true);
158162306a36Sopenharmony_ci			continue;
158262306a36Sopenharmony_ci		} else if (reloc->sym->sec->idx) {
158362306a36Sopenharmony_ci			dest_sec = reloc->sym->sec;
158462306a36Sopenharmony_ci			dest_off = reloc->sym->sym.st_value +
158562306a36Sopenharmony_ci				   arch_dest_reloc_offset(reloc_addend(reloc));
158662306a36Sopenharmony_ci		} else {
158762306a36Sopenharmony_ci			/* non-func asm code jumping to another file */
158862306a36Sopenharmony_ci			continue;
158962306a36Sopenharmony_ci		}
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci		jump_dest = find_insn(file, dest_sec, dest_off);
159262306a36Sopenharmony_ci		if (!jump_dest) {
159362306a36Sopenharmony_ci			struct symbol *sym = find_symbol_by_offset(dest_sec, dest_off);
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci			/*
159662306a36Sopenharmony_ci			 * This is a special case for retbleed_untrain_ret().
159762306a36Sopenharmony_ci			 * It jumps to __x86_return_thunk(), but objtool
159862306a36Sopenharmony_ci			 * can't find the thunk's starting RET
159962306a36Sopenharmony_ci			 * instruction, because the RET is also in the
160062306a36Sopenharmony_ci			 * middle of another instruction.  Objtool only
160162306a36Sopenharmony_ci			 * knows about the outer instruction.
160262306a36Sopenharmony_ci			 */
160362306a36Sopenharmony_ci			if (sym && sym->embedded_insn) {
160462306a36Sopenharmony_ci				add_return_call(file, insn, false);
160562306a36Sopenharmony_ci				continue;
160662306a36Sopenharmony_ci			}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci			WARN_INSN(insn, "can't find jump dest instruction at %s+0x%lx",
160962306a36Sopenharmony_ci				  dest_sec->name, dest_off);
161062306a36Sopenharmony_ci			return -1;
161162306a36Sopenharmony_ci		}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci		/*
161462306a36Sopenharmony_ci		 * Cross-function jump.
161562306a36Sopenharmony_ci		 */
161662306a36Sopenharmony_ci		if (insn_func(insn) && insn_func(jump_dest) &&
161762306a36Sopenharmony_ci		    insn_func(insn) != insn_func(jump_dest)) {
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci			/*
162062306a36Sopenharmony_ci			 * For GCC 8+, create parent/child links for any cold
162162306a36Sopenharmony_ci			 * subfunctions.  This is _mostly_ redundant with a
162262306a36Sopenharmony_ci			 * similar initialization in read_symbols().
162362306a36Sopenharmony_ci			 *
162462306a36Sopenharmony_ci			 * If a function has aliases, we want the *first* such
162562306a36Sopenharmony_ci			 * function in the symbol table to be the subfunction's
162662306a36Sopenharmony_ci			 * parent.  In that case we overwrite the
162762306a36Sopenharmony_ci			 * initialization done in read_symbols().
162862306a36Sopenharmony_ci			 *
162962306a36Sopenharmony_ci			 * However this code can't completely replace the
163062306a36Sopenharmony_ci			 * read_symbols() code because this doesn't detect the
163162306a36Sopenharmony_ci			 * case where the parent function's only reference to a
163262306a36Sopenharmony_ci			 * subfunction is through a jump table.
163362306a36Sopenharmony_ci			 */
163462306a36Sopenharmony_ci			if (!strstr(insn_func(insn)->name, ".cold") &&
163562306a36Sopenharmony_ci			    strstr(insn_func(jump_dest)->name, ".cold")) {
163662306a36Sopenharmony_ci				insn_func(insn)->cfunc = insn_func(jump_dest);
163762306a36Sopenharmony_ci				insn_func(jump_dest)->pfunc = insn_func(insn);
163862306a36Sopenharmony_ci			}
163962306a36Sopenharmony_ci		}
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci		if (jump_is_sibling_call(file, insn, jump_dest)) {
164262306a36Sopenharmony_ci			/*
164362306a36Sopenharmony_ci			 * Internal sibling call without reloc or with
164462306a36Sopenharmony_ci			 * STT_SECTION reloc.
164562306a36Sopenharmony_ci			 */
164662306a36Sopenharmony_ci			add_call_dest(file, insn, insn_func(jump_dest), true);
164762306a36Sopenharmony_ci			continue;
164862306a36Sopenharmony_ci		}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci		insn->jump_dest = jump_dest;
165162306a36Sopenharmony_ci	}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	return 0;
165462306a36Sopenharmony_ci}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_cistatic struct symbol *find_call_destination(struct section *sec, unsigned long offset)
165762306a36Sopenharmony_ci{
165862306a36Sopenharmony_ci	struct symbol *call_dest;
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	call_dest = find_func_by_offset(sec, offset);
166162306a36Sopenharmony_ci	if (!call_dest)
166262306a36Sopenharmony_ci		call_dest = find_symbol_by_offset(sec, offset);
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	return call_dest;
166562306a36Sopenharmony_ci}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci/*
166862306a36Sopenharmony_ci * Find the destination instructions for all calls.
166962306a36Sopenharmony_ci */
167062306a36Sopenharmony_cistatic int add_call_destinations(struct objtool_file *file)
167162306a36Sopenharmony_ci{
167262306a36Sopenharmony_ci	struct instruction *insn;
167362306a36Sopenharmony_ci	unsigned long dest_off;
167462306a36Sopenharmony_ci	struct symbol *dest;
167562306a36Sopenharmony_ci	struct reloc *reloc;
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	for_each_insn(file, insn) {
167862306a36Sopenharmony_ci		if (insn->type != INSN_CALL)
167962306a36Sopenharmony_ci			continue;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci		reloc = insn_reloc(file, insn);
168262306a36Sopenharmony_ci		if (!reloc) {
168362306a36Sopenharmony_ci			dest_off = arch_jump_destination(insn);
168462306a36Sopenharmony_ci			dest = find_call_destination(insn->sec, dest_off);
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci			add_call_dest(file, insn, dest, false);
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci			if (insn->ignore)
168962306a36Sopenharmony_ci				continue;
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci			if (!insn_call_dest(insn)) {
169262306a36Sopenharmony_ci				WARN_INSN(insn, "unannotated intra-function call");
169362306a36Sopenharmony_ci				return -1;
169462306a36Sopenharmony_ci			}
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci			if (insn_func(insn) && insn_call_dest(insn)->type != STT_FUNC) {
169762306a36Sopenharmony_ci				WARN_INSN(insn, "unsupported call to non-function");
169862306a36Sopenharmony_ci				return -1;
169962306a36Sopenharmony_ci			}
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci		} else if (reloc->sym->type == STT_SECTION) {
170262306a36Sopenharmony_ci			dest_off = arch_dest_reloc_offset(reloc_addend(reloc));
170362306a36Sopenharmony_ci			dest = find_call_destination(reloc->sym->sec, dest_off);
170462306a36Sopenharmony_ci			if (!dest) {
170562306a36Sopenharmony_ci				WARN_INSN(insn, "can't find call dest symbol at %s+0x%lx",
170662306a36Sopenharmony_ci					  reloc->sym->sec->name, dest_off);
170762306a36Sopenharmony_ci				return -1;
170862306a36Sopenharmony_ci			}
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci			add_call_dest(file, insn, dest, false);
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci		} else if (reloc->sym->retpoline_thunk) {
171362306a36Sopenharmony_ci			add_retpoline_call(file, insn);
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci		} else
171662306a36Sopenharmony_ci			add_call_dest(file, insn, reloc->sym, false);
171762306a36Sopenharmony_ci	}
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	return 0;
172062306a36Sopenharmony_ci}
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci/*
172362306a36Sopenharmony_ci * The .alternatives section requires some extra special care over and above
172462306a36Sopenharmony_ci * other special sections because alternatives are patched in place.
172562306a36Sopenharmony_ci */
172662306a36Sopenharmony_cistatic int handle_group_alt(struct objtool_file *file,
172762306a36Sopenharmony_ci			    struct special_alt *special_alt,
172862306a36Sopenharmony_ci			    struct instruction *orig_insn,
172962306a36Sopenharmony_ci			    struct instruction **new_insn)
173062306a36Sopenharmony_ci{
173162306a36Sopenharmony_ci	struct instruction *last_new_insn = NULL, *insn, *nop = NULL;
173262306a36Sopenharmony_ci	struct alt_group *orig_alt_group, *new_alt_group;
173362306a36Sopenharmony_ci	unsigned long dest_off;
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	orig_alt_group = orig_insn->alt_group;
173662306a36Sopenharmony_ci	if (!orig_alt_group) {
173762306a36Sopenharmony_ci		struct instruction *last_orig_insn = NULL;
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci		orig_alt_group = malloc(sizeof(*orig_alt_group));
174062306a36Sopenharmony_ci		if (!orig_alt_group) {
174162306a36Sopenharmony_ci			WARN("malloc failed");
174262306a36Sopenharmony_ci			return -1;
174362306a36Sopenharmony_ci		}
174462306a36Sopenharmony_ci		orig_alt_group->cfi = calloc(special_alt->orig_len,
174562306a36Sopenharmony_ci					     sizeof(struct cfi_state *));
174662306a36Sopenharmony_ci		if (!orig_alt_group->cfi) {
174762306a36Sopenharmony_ci			WARN("calloc failed");
174862306a36Sopenharmony_ci			return -1;
174962306a36Sopenharmony_ci		}
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci		insn = orig_insn;
175262306a36Sopenharmony_ci		sec_for_each_insn_from(file, insn) {
175362306a36Sopenharmony_ci			if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
175462306a36Sopenharmony_ci				break;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci			insn->alt_group = orig_alt_group;
175762306a36Sopenharmony_ci			last_orig_insn = insn;
175862306a36Sopenharmony_ci		}
175962306a36Sopenharmony_ci		orig_alt_group->orig_group = NULL;
176062306a36Sopenharmony_ci		orig_alt_group->first_insn = orig_insn;
176162306a36Sopenharmony_ci		orig_alt_group->last_insn = last_orig_insn;
176262306a36Sopenharmony_ci		orig_alt_group->nop = NULL;
176362306a36Sopenharmony_ci	} else {
176462306a36Sopenharmony_ci		if (orig_alt_group->last_insn->offset + orig_alt_group->last_insn->len -
176562306a36Sopenharmony_ci		    orig_alt_group->first_insn->offset != special_alt->orig_len) {
176662306a36Sopenharmony_ci			WARN_INSN(orig_insn, "weirdly overlapping alternative! %ld != %d",
176762306a36Sopenharmony_ci				  orig_alt_group->last_insn->offset +
176862306a36Sopenharmony_ci				  orig_alt_group->last_insn->len -
176962306a36Sopenharmony_ci				  orig_alt_group->first_insn->offset,
177062306a36Sopenharmony_ci				  special_alt->orig_len);
177162306a36Sopenharmony_ci			return -1;
177262306a36Sopenharmony_ci		}
177362306a36Sopenharmony_ci	}
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	new_alt_group = malloc(sizeof(*new_alt_group));
177662306a36Sopenharmony_ci	if (!new_alt_group) {
177762306a36Sopenharmony_ci		WARN("malloc failed");
177862306a36Sopenharmony_ci		return -1;
177962306a36Sopenharmony_ci	}
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	if (special_alt->new_len < special_alt->orig_len) {
178262306a36Sopenharmony_ci		/*
178362306a36Sopenharmony_ci		 * Insert a fake nop at the end to make the replacement
178462306a36Sopenharmony_ci		 * alt_group the same size as the original.  This is needed to
178562306a36Sopenharmony_ci		 * allow propagate_alt_cfi() to do its magic.  When the last
178662306a36Sopenharmony_ci		 * instruction affects the stack, the instruction after it (the
178762306a36Sopenharmony_ci		 * nop) will propagate the new state to the shared CFI array.
178862306a36Sopenharmony_ci		 */
178962306a36Sopenharmony_ci		nop = malloc(sizeof(*nop));
179062306a36Sopenharmony_ci		if (!nop) {
179162306a36Sopenharmony_ci			WARN("malloc failed");
179262306a36Sopenharmony_ci			return -1;
179362306a36Sopenharmony_ci		}
179462306a36Sopenharmony_ci		memset(nop, 0, sizeof(*nop));
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci		nop->sec = special_alt->new_sec;
179762306a36Sopenharmony_ci		nop->offset = special_alt->new_off + special_alt->new_len;
179862306a36Sopenharmony_ci		nop->len = special_alt->orig_len - special_alt->new_len;
179962306a36Sopenharmony_ci		nop->type = INSN_NOP;
180062306a36Sopenharmony_ci		nop->sym = orig_insn->sym;
180162306a36Sopenharmony_ci		nop->alt_group = new_alt_group;
180262306a36Sopenharmony_ci		nop->ignore = orig_insn->ignore_alts;
180362306a36Sopenharmony_ci	}
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	if (!special_alt->new_len) {
180662306a36Sopenharmony_ci		*new_insn = nop;
180762306a36Sopenharmony_ci		goto end;
180862306a36Sopenharmony_ci	}
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	insn = *new_insn;
181162306a36Sopenharmony_ci	sec_for_each_insn_from(file, insn) {
181262306a36Sopenharmony_ci		struct reloc *alt_reloc;
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci		if (insn->offset >= special_alt->new_off + special_alt->new_len)
181562306a36Sopenharmony_ci			break;
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci		last_new_insn = insn;
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci		insn->ignore = orig_insn->ignore_alts;
182062306a36Sopenharmony_ci		insn->sym = orig_insn->sym;
182162306a36Sopenharmony_ci		insn->alt_group = new_alt_group;
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci		/*
182462306a36Sopenharmony_ci		 * Since alternative replacement code is copy/pasted by the
182562306a36Sopenharmony_ci		 * kernel after applying relocations, generally such code can't
182662306a36Sopenharmony_ci		 * have relative-address relocation references to outside the
182762306a36Sopenharmony_ci		 * .altinstr_replacement section, unless the arch's
182862306a36Sopenharmony_ci		 * alternatives code can adjust the relative offsets
182962306a36Sopenharmony_ci		 * accordingly.
183062306a36Sopenharmony_ci		 */
183162306a36Sopenharmony_ci		alt_reloc = insn_reloc(file, insn);
183262306a36Sopenharmony_ci		if (alt_reloc && arch_pc_relative_reloc(alt_reloc) &&
183362306a36Sopenharmony_ci		    !arch_support_alt_relocation(special_alt, insn, alt_reloc)) {
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci			WARN_INSN(insn, "unsupported relocation in alternatives section");
183662306a36Sopenharmony_ci			return -1;
183762306a36Sopenharmony_ci		}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci		if (!is_static_jump(insn))
184062306a36Sopenharmony_ci			continue;
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci		if (!insn->immediate)
184362306a36Sopenharmony_ci			continue;
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci		dest_off = arch_jump_destination(insn);
184662306a36Sopenharmony_ci		if (dest_off == special_alt->new_off + special_alt->new_len) {
184762306a36Sopenharmony_ci			insn->jump_dest = next_insn_same_sec(file, orig_alt_group->last_insn);
184862306a36Sopenharmony_ci			if (!insn->jump_dest) {
184962306a36Sopenharmony_ci				WARN_INSN(insn, "can't find alternative jump destination");
185062306a36Sopenharmony_ci				return -1;
185162306a36Sopenharmony_ci			}
185262306a36Sopenharmony_ci		}
185362306a36Sopenharmony_ci	}
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	if (!last_new_insn) {
185662306a36Sopenharmony_ci		WARN_FUNC("can't find last new alternative instruction",
185762306a36Sopenharmony_ci			  special_alt->new_sec, special_alt->new_off);
185862306a36Sopenharmony_ci		return -1;
185962306a36Sopenharmony_ci	}
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ciend:
186262306a36Sopenharmony_ci	new_alt_group->orig_group = orig_alt_group;
186362306a36Sopenharmony_ci	new_alt_group->first_insn = *new_insn;
186462306a36Sopenharmony_ci	new_alt_group->last_insn = last_new_insn;
186562306a36Sopenharmony_ci	new_alt_group->nop = nop;
186662306a36Sopenharmony_ci	new_alt_group->cfi = orig_alt_group->cfi;
186762306a36Sopenharmony_ci	return 0;
186862306a36Sopenharmony_ci}
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci/*
187162306a36Sopenharmony_ci * A jump table entry can either convert a nop to a jump or a jump to a nop.
187262306a36Sopenharmony_ci * If the original instruction is a jump, make the alt entry an effective nop
187362306a36Sopenharmony_ci * by just skipping the original instruction.
187462306a36Sopenharmony_ci */
187562306a36Sopenharmony_cistatic int handle_jump_alt(struct objtool_file *file,
187662306a36Sopenharmony_ci			   struct special_alt *special_alt,
187762306a36Sopenharmony_ci			   struct instruction *orig_insn,
187862306a36Sopenharmony_ci			   struct instruction **new_insn)
187962306a36Sopenharmony_ci{
188062306a36Sopenharmony_ci	if (orig_insn->type != INSN_JUMP_UNCONDITIONAL &&
188162306a36Sopenharmony_ci	    orig_insn->type != INSN_NOP) {
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci		WARN_INSN(orig_insn, "unsupported instruction at jump label");
188462306a36Sopenharmony_ci		return -1;
188562306a36Sopenharmony_ci	}
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	if (opts.hack_jump_label && special_alt->key_addend & 2) {
188862306a36Sopenharmony_ci		struct reloc *reloc = insn_reloc(file, orig_insn);
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci		if (reloc)
189162306a36Sopenharmony_ci			set_reloc_type(file->elf, reloc, R_NONE);
189262306a36Sopenharmony_ci		elf_write_insn(file->elf, orig_insn->sec,
189362306a36Sopenharmony_ci			       orig_insn->offset, orig_insn->len,
189462306a36Sopenharmony_ci			       arch_nop_insn(orig_insn->len));
189562306a36Sopenharmony_ci		orig_insn->type = INSN_NOP;
189662306a36Sopenharmony_ci	}
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	if (orig_insn->type == INSN_NOP) {
189962306a36Sopenharmony_ci		if (orig_insn->len == 2)
190062306a36Sopenharmony_ci			file->jl_nop_short++;
190162306a36Sopenharmony_ci		else
190262306a36Sopenharmony_ci			file->jl_nop_long++;
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci		return 0;
190562306a36Sopenharmony_ci	}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	if (orig_insn->len == 2)
190862306a36Sopenharmony_ci		file->jl_short++;
190962306a36Sopenharmony_ci	else
191062306a36Sopenharmony_ci		file->jl_long++;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	*new_insn = next_insn_same_sec(file, orig_insn);
191362306a36Sopenharmony_ci	return 0;
191462306a36Sopenharmony_ci}
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci/*
191762306a36Sopenharmony_ci * Read all the special sections which have alternate instructions which can be
191862306a36Sopenharmony_ci * patched in or redirected to at runtime.  Each instruction having alternate
191962306a36Sopenharmony_ci * instruction(s) has them added to its insn->alts list, which will be
192062306a36Sopenharmony_ci * traversed in validate_branch().
192162306a36Sopenharmony_ci */
192262306a36Sopenharmony_cistatic int add_special_section_alts(struct objtool_file *file)
192362306a36Sopenharmony_ci{
192462306a36Sopenharmony_ci	struct list_head special_alts;
192562306a36Sopenharmony_ci	struct instruction *orig_insn, *new_insn;
192662306a36Sopenharmony_ci	struct special_alt *special_alt, *tmp;
192762306a36Sopenharmony_ci	struct alternative *alt;
192862306a36Sopenharmony_ci	int ret;
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	ret = special_get_alts(file->elf, &special_alts);
193162306a36Sopenharmony_ci	if (ret)
193262306a36Sopenharmony_ci		return ret;
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci		orig_insn = find_insn(file, special_alt->orig_sec,
193762306a36Sopenharmony_ci				      special_alt->orig_off);
193862306a36Sopenharmony_ci		if (!orig_insn) {
193962306a36Sopenharmony_ci			WARN_FUNC("special: can't find orig instruction",
194062306a36Sopenharmony_ci				  special_alt->orig_sec, special_alt->orig_off);
194162306a36Sopenharmony_ci			ret = -1;
194262306a36Sopenharmony_ci			goto out;
194362306a36Sopenharmony_ci		}
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci		new_insn = NULL;
194662306a36Sopenharmony_ci		if (!special_alt->group || special_alt->new_len) {
194762306a36Sopenharmony_ci			new_insn = find_insn(file, special_alt->new_sec,
194862306a36Sopenharmony_ci					     special_alt->new_off);
194962306a36Sopenharmony_ci			if (!new_insn) {
195062306a36Sopenharmony_ci				WARN_FUNC("special: can't find new instruction",
195162306a36Sopenharmony_ci					  special_alt->new_sec,
195262306a36Sopenharmony_ci					  special_alt->new_off);
195362306a36Sopenharmony_ci				ret = -1;
195462306a36Sopenharmony_ci				goto out;
195562306a36Sopenharmony_ci			}
195662306a36Sopenharmony_ci		}
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci		if (special_alt->group) {
195962306a36Sopenharmony_ci			if (!special_alt->orig_len) {
196062306a36Sopenharmony_ci				WARN_INSN(orig_insn, "empty alternative entry");
196162306a36Sopenharmony_ci				continue;
196262306a36Sopenharmony_ci			}
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci			ret = handle_group_alt(file, special_alt, orig_insn,
196562306a36Sopenharmony_ci					       &new_insn);
196662306a36Sopenharmony_ci			if (ret)
196762306a36Sopenharmony_ci				goto out;
196862306a36Sopenharmony_ci		} else if (special_alt->jump_or_nop) {
196962306a36Sopenharmony_ci			ret = handle_jump_alt(file, special_alt, orig_insn,
197062306a36Sopenharmony_ci					      &new_insn);
197162306a36Sopenharmony_ci			if (ret)
197262306a36Sopenharmony_ci				goto out;
197362306a36Sopenharmony_ci		}
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci		alt = malloc(sizeof(*alt));
197662306a36Sopenharmony_ci		if (!alt) {
197762306a36Sopenharmony_ci			WARN("malloc failed");
197862306a36Sopenharmony_ci			ret = -1;
197962306a36Sopenharmony_ci			goto out;
198062306a36Sopenharmony_ci		}
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci		alt->insn = new_insn;
198362306a36Sopenharmony_ci		alt->skip_orig = special_alt->skip_orig;
198462306a36Sopenharmony_ci		orig_insn->ignore_alts |= special_alt->skip_alt;
198562306a36Sopenharmony_ci		alt->next = orig_insn->alts;
198662306a36Sopenharmony_ci		orig_insn->alts = alt;
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci		list_del(&special_alt->list);
198962306a36Sopenharmony_ci		free(special_alt);
199062306a36Sopenharmony_ci	}
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	if (opts.stats) {
199362306a36Sopenharmony_ci		printf("jl\\\tNOP\tJMP\n");
199462306a36Sopenharmony_ci		printf("short:\t%ld\t%ld\n", file->jl_nop_short, file->jl_short);
199562306a36Sopenharmony_ci		printf("long:\t%ld\t%ld\n", file->jl_nop_long, file->jl_long);
199662306a36Sopenharmony_ci	}
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ciout:
199962306a36Sopenharmony_ci	return ret;
200062306a36Sopenharmony_ci}
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_cistatic int add_jump_table(struct objtool_file *file, struct instruction *insn,
200362306a36Sopenharmony_ci			  struct reloc *next_table)
200462306a36Sopenharmony_ci{
200562306a36Sopenharmony_ci	struct symbol *pfunc = insn_func(insn)->pfunc;
200662306a36Sopenharmony_ci	struct reloc *table = insn_jump_table(insn);
200762306a36Sopenharmony_ci	struct instruction *dest_insn;
200862306a36Sopenharmony_ci	unsigned int prev_offset = 0;
200962306a36Sopenharmony_ci	struct reloc *reloc = table;
201062306a36Sopenharmony_ci	struct alternative *alt;
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	/*
201362306a36Sopenharmony_ci	 * Each @reloc is a switch table relocation which points to the target
201462306a36Sopenharmony_ci	 * instruction.
201562306a36Sopenharmony_ci	 */
201662306a36Sopenharmony_ci	for_each_reloc_from(table->sec, reloc) {
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci		/* Check for the end of the table: */
201962306a36Sopenharmony_ci		if (reloc != table && reloc == next_table)
202062306a36Sopenharmony_ci			break;
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci		/* Make sure the table entries are consecutive: */
202362306a36Sopenharmony_ci		if (prev_offset && reloc_offset(reloc) != prev_offset + 8)
202462306a36Sopenharmony_ci			break;
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci		/* Detect function pointers from contiguous objects: */
202762306a36Sopenharmony_ci		if (reloc->sym->sec == pfunc->sec &&
202862306a36Sopenharmony_ci		    reloc_addend(reloc) == pfunc->offset)
202962306a36Sopenharmony_ci			break;
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci		dest_insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
203262306a36Sopenharmony_ci		if (!dest_insn)
203362306a36Sopenharmony_ci			break;
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci		/* Make sure the destination is in the same function: */
203662306a36Sopenharmony_ci		if (!insn_func(dest_insn) || insn_func(dest_insn)->pfunc != pfunc)
203762306a36Sopenharmony_ci			break;
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci		alt = malloc(sizeof(*alt));
204062306a36Sopenharmony_ci		if (!alt) {
204162306a36Sopenharmony_ci			WARN("malloc failed");
204262306a36Sopenharmony_ci			return -1;
204362306a36Sopenharmony_ci		}
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci		alt->insn = dest_insn;
204662306a36Sopenharmony_ci		alt->next = insn->alts;
204762306a36Sopenharmony_ci		insn->alts = alt;
204862306a36Sopenharmony_ci		prev_offset = reloc_offset(reloc);
204962306a36Sopenharmony_ci	}
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	if (!prev_offset) {
205262306a36Sopenharmony_ci		WARN_INSN(insn, "can't find switch jump table");
205362306a36Sopenharmony_ci		return -1;
205462306a36Sopenharmony_ci	}
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	return 0;
205762306a36Sopenharmony_ci}
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci/*
206062306a36Sopenharmony_ci * find_jump_table() - Given a dynamic jump, find the switch jump table
206162306a36Sopenharmony_ci * associated with it.
206262306a36Sopenharmony_ci */
206362306a36Sopenharmony_cistatic struct reloc *find_jump_table(struct objtool_file *file,
206462306a36Sopenharmony_ci				      struct symbol *func,
206562306a36Sopenharmony_ci				      struct instruction *insn)
206662306a36Sopenharmony_ci{
206762306a36Sopenharmony_ci	struct reloc *table_reloc;
206862306a36Sopenharmony_ci	struct instruction *dest_insn, *orig_insn = insn;
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	/*
207162306a36Sopenharmony_ci	 * Backward search using the @first_jump_src links, these help avoid
207262306a36Sopenharmony_ci	 * much of the 'in between' code. Which avoids us getting confused by
207362306a36Sopenharmony_ci	 * it.
207462306a36Sopenharmony_ci	 */
207562306a36Sopenharmony_ci	for (;
207662306a36Sopenharmony_ci	     insn && insn_func(insn) && insn_func(insn)->pfunc == func;
207762306a36Sopenharmony_ci	     insn = insn->first_jump_src ?: prev_insn_same_sym(file, insn)) {
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci		if (insn != orig_insn && insn->type == INSN_JUMP_DYNAMIC)
208062306a36Sopenharmony_ci			break;
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci		/* allow small jumps within the range */
208362306a36Sopenharmony_ci		if (insn->type == INSN_JUMP_UNCONDITIONAL &&
208462306a36Sopenharmony_ci		    insn->jump_dest &&
208562306a36Sopenharmony_ci		    (insn->jump_dest->offset <= insn->offset ||
208662306a36Sopenharmony_ci		     insn->jump_dest->offset > orig_insn->offset))
208762306a36Sopenharmony_ci		    break;
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci		table_reloc = arch_find_switch_table(file, insn);
209062306a36Sopenharmony_ci		if (!table_reloc)
209162306a36Sopenharmony_ci			continue;
209262306a36Sopenharmony_ci		dest_insn = find_insn(file, table_reloc->sym->sec, reloc_addend(table_reloc));
209362306a36Sopenharmony_ci		if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func)
209462306a36Sopenharmony_ci			continue;
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci		return table_reloc;
209762306a36Sopenharmony_ci	}
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	return NULL;
210062306a36Sopenharmony_ci}
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci/*
210362306a36Sopenharmony_ci * First pass: Mark the head of each jump table so that in the next pass,
210462306a36Sopenharmony_ci * we know when a given jump table ends and the next one starts.
210562306a36Sopenharmony_ci */
210662306a36Sopenharmony_cistatic void mark_func_jump_tables(struct objtool_file *file,
210762306a36Sopenharmony_ci				    struct symbol *func)
210862306a36Sopenharmony_ci{
210962306a36Sopenharmony_ci	struct instruction *insn, *last = NULL;
211062306a36Sopenharmony_ci	struct reloc *reloc;
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci	func_for_each_insn(file, func, insn) {
211362306a36Sopenharmony_ci		if (!last)
211462306a36Sopenharmony_ci			last = insn;
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci		/*
211762306a36Sopenharmony_ci		 * Store back-pointers for unconditional forward jumps such
211862306a36Sopenharmony_ci		 * that find_jump_table() can back-track using those and
211962306a36Sopenharmony_ci		 * avoid some potentially confusing code.
212062306a36Sopenharmony_ci		 */
212162306a36Sopenharmony_ci		if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest &&
212262306a36Sopenharmony_ci		    insn->offset > last->offset &&
212362306a36Sopenharmony_ci		    insn->jump_dest->offset > insn->offset &&
212462306a36Sopenharmony_ci		    !insn->jump_dest->first_jump_src) {
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci			insn->jump_dest->first_jump_src = insn;
212762306a36Sopenharmony_ci			last = insn->jump_dest;
212862306a36Sopenharmony_ci		}
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci		if (insn->type != INSN_JUMP_DYNAMIC)
213162306a36Sopenharmony_ci			continue;
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci		reloc = find_jump_table(file, func, insn);
213462306a36Sopenharmony_ci		if (reloc)
213562306a36Sopenharmony_ci			insn->_jump_table = reloc;
213662306a36Sopenharmony_ci	}
213762306a36Sopenharmony_ci}
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_cistatic int add_func_jump_tables(struct objtool_file *file,
214062306a36Sopenharmony_ci				  struct symbol *func)
214162306a36Sopenharmony_ci{
214262306a36Sopenharmony_ci	struct instruction *insn, *insn_t1 = NULL, *insn_t2;
214362306a36Sopenharmony_ci	int ret = 0;
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci	func_for_each_insn(file, func, insn) {
214662306a36Sopenharmony_ci		if (!insn_jump_table(insn))
214762306a36Sopenharmony_ci			continue;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci		if (!insn_t1) {
215062306a36Sopenharmony_ci			insn_t1 = insn;
215162306a36Sopenharmony_ci			continue;
215262306a36Sopenharmony_ci		}
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci		insn_t2 = insn;
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci		ret = add_jump_table(file, insn_t1, insn_jump_table(insn_t2));
215762306a36Sopenharmony_ci		if (ret)
215862306a36Sopenharmony_ci			return ret;
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci		insn_t1 = insn_t2;
216162306a36Sopenharmony_ci	}
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	if (insn_t1)
216462306a36Sopenharmony_ci		ret = add_jump_table(file, insn_t1, NULL);
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	return ret;
216762306a36Sopenharmony_ci}
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci/*
217062306a36Sopenharmony_ci * For some switch statements, gcc generates a jump table in the .rodata
217162306a36Sopenharmony_ci * section which contains a list of addresses within the function to jump to.
217262306a36Sopenharmony_ci * This finds these jump tables and adds them to the insn->alts lists.
217362306a36Sopenharmony_ci */
217462306a36Sopenharmony_cistatic int add_jump_table_alts(struct objtool_file *file)
217562306a36Sopenharmony_ci{
217662306a36Sopenharmony_ci	struct symbol *func;
217762306a36Sopenharmony_ci	int ret;
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	if (!file->rodata)
218062306a36Sopenharmony_ci		return 0;
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci	for_each_sym(file, func) {
218362306a36Sopenharmony_ci		if (func->type != STT_FUNC)
218462306a36Sopenharmony_ci			continue;
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci		mark_func_jump_tables(file, func);
218762306a36Sopenharmony_ci		ret = add_func_jump_tables(file, func);
218862306a36Sopenharmony_ci		if (ret)
218962306a36Sopenharmony_ci			return ret;
219062306a36Sopenharmony_ci	}
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci	return 0;
219362306a36Sopenharmony_ci}
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_cistatic void set_func_state(struct cfi_state *state)
219662306a36Sopenharmony_ci{
219762306a36Sopenharmony_ci	state->cfa = initial_func_cfi.cfa;
219862306a36Sopenharmony_ci	memcpy(&state->regs, &initial_func_cfi.regs,
219962306a36Sopenharmony_ci	       CFI_NUM_REGS * sizeof(struct cfi_reg));
220062306a36Sopenharmony_ci	state->stack_size = initial_func_cfi.cfa.offset;
220162306a36Sopenharmony_ci	state->type = UNWIND_HINT_TYPE_CALL;
220262306a36Sopenharmony_ci}
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_cistatic int read_unwind_hints(struct objtool_file *file)
220562306a36Sopenharmony_ci{
220662306a36Sopenharmony_ci	struct cfi_state cfi = init_cfi;
220762306a36Sopenharmony_ci	struct section *sec;
220862306a36Sopenharmony_ci	struct unwind_hint *hint;
220962306a36Sopenharmony_ci	struct instruction *insn;
221062306a36Sopenharmony_ci	struct reloc *reloc;
221162306a36Sopenharmony_ci	int i;
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	sec = find_section_by_name(file->elf, ".discard.unwind_hints");
221462306a36Sopenharmony_ci	if (!sec)
221562306a36Sopenharmony_ci		return 0;
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	if (!sec->rsec) {
221862306a36Sopenharmony_ci		WARN("missing .rela.discard.unwind_hints section");
221962306a36Sopenharmony_ci		return -1;
222062306a36Sopenharmony_ci	}
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci	if (sec->sh.sh_size % sizeof(struct unwind_hint)) {
222362306a36Sopenharmony_ci		WARN("struct unwind_hint size mismatch");
222462306a36Sopenharmony_ci		return -1;
222562306a36Sopenharmony_ci	}
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	file->hints = true;
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci	for (i = 0; i < sec->sh.sh_size / sizeof(struct unwind_hint); i++) {
223062306a36Sopenharmony_ci		hint = (struct unwind_hint *)sec->data->d_buf + i;
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci		reloc = find_reloc_by_dest(file->elf, sec, i * sizeof(*hint));
223362306a36Sopenharmony_ci		if (!reloc) {
223462306a36Sopenharmony_ci			WARN("can't find reloc for unwind_hints[%d]", i);
223562306a36Sopenharmony_ci			return -1;
223662306a36Sopenharmony_ci		}
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci		insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
223962306a36Sopenharmony_ci		if (!insn) {
224062306a36Sopenharmony_ci			WARN("can't find insn for unwind_hints[%d]", i);
224162306a36Sopenharmony_ci			return -1;
224262306a36Sopenharmony_ci		}
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci		insn->hint = true;
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci		if (hint->type == UNWIND_HINT_TYPE_UNDEFINED) {
224762306a36Sopenharmony_ci			insn->cfi = &force_undefined_cfi;
224862306a36Sopenharmony_ci			continue;
224962306a36Sopenharmony_ci		}
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci		if (hint->type == UNWIND_HINT_TYPE_SAVE) {
225262306a36Sopenharmony_ci			insn->hint = false;
225362306a36Sopenharmony_ci			insn->save = true;
225462306a36Sopenharmony_ci			continue;
225562306a36Sopenharmony_ci		}
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci		if (hint->type == UNWIND_HINT_TYPE_RESTORE) {
225862306a36Sopenharmony_ci			insn->restore = true;
225962306a36Sopenharmony_ci			continue;
226062306a36Sopenharmony_ci		}
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci		if (hint->type == UNWIND_HINT_TYPE_REGS_PARTIAL) {
226362306a36Sopenharmony_ci			struct symbol *sym = find_symbol_by_offset(insn->sec, insn->offset);
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci			if (sym && sym->bind == STB_GLOBAL) {
226662306a36Sopenharmony_ci				if (opts.ibt && insn->type != INSN_ENDBR && !insn->noendbr) {
226762306a36Sopenharmony_ci					WARN_INSN(insn, "UNWIND_HINT_IRET_REGS without ENDBR");
226862306a36Sopenharmony_ci				}
226962306a36Sopenharmony_ci			}
227062306a36Sopenharmony_ci		}
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci		if (hint->type == UNWIND_HINT_TYPE_FUNC) {
227362306a36Sopenharmony_ci			insn->cfi = &func_cfi;
227462306a36Sopenharmony_ci			continue;
227562306a36Sopenharmony_ci		}
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci		if (insn->cfi)
227862306a36Sopenharmony_ci			cfi = *(insn->cfi);
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci		if (arch_decode_hint_reg(hint->sp_reg, &cfi.cfa.base)) {
228162306a36Sopenharmony_ci			WARN_INSN(insn, "unsupported unwind_hint sp base reg %d", hint->sp_reg);
228262306a36Sopenharmony_ci			return -1;
228362306a36Sopenharmony_ci		}
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci		cfi.cfa.offset = bswap_if_needed(file->elf, hint->sp_offset);
228662306a36Sopenharmony_ci		cfi.type = hint->type;
228762306a36Sopenharmony_ci		cfi.signal = hint->signal;
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci		insn->cfi = cfi_hash_find_or_add(&cfi);
229062306a36Sopenharmony_ci	}
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	return 0;
229362306a36Sopenharmony_ci}
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_cistatic int read_noendbr_hints(struct objtool_file *file)
229662306a36Sopenharmony_ci{
229762306a36Sopenharmony_ci	struct instruction *insn;
229862306a36Sopenharmony_ci	struct section *rsec;
229962306a36Sopenharmony_ci	struct reloc *reloc;
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	rsec = find_section_by_name(file->elf, ".rela.discard.noendbr");
230262306a36Sopenharmony_ci	if (!rsec)
230362306a36Sopenharmony_ci		return 0;
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci	for_each_reloc(rsec, reloc) {
230662306a36Sopenharmony_ci		insn = find_insn(file, reloc->sym->sec,
230762306a36Sopenharmony_ci				 reloc->sym->offset + reloc_addend(reloc));
230862306a36Sopenharmony_ci		if (!insn) {
230962306a36Sopenharmony_ci			WARN("bad .discard.noendbr entry");
231062306a36Sopenharmony_ci			return -1;
231162306a36Sopenharmony_ci		}
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci		insn->noendbr = 1;
231462306a36Sopenharmony_ci	}
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	return 0;
231762306a36Sopenharmony_ci}
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_cistatic int read_retpoline_hints(struct objtool_file *file)
232062306a36Sopenharmony_ci{
232162306a36Sopenharmony_ci	struct section *rsec;
232262306a36Sopenharmony_ci	struct instruction *insn;
232362306a36Sopenharmony_ci	struct reloc *reloc;
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_ci	rsec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe");
232662306a36Sopenharmony_ci	if (!rsec)
232762306a36Sopenharmony_ci		return 0;
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci	for_each_reloc(rsec, reloc) {
233062306a36Sopenharmony_ci		if (reloc->sym->type != STT_SECTION) {
233162306a36Sopenharmony_ci			WARN("unexpected relocation symbol type in %s", rsec->name);
233262306a36Sopenharmony_ci			return -1;
233362306a36Sopenharmony_ci		}
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci		insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
233662306a36Sopenharmony_ci		if (!insn) {
233762306a36Sopenharmony_ci			WARN("bad .discard.retpoline_safe entry");
233862306a36Sopenharmony_ci			return -1;
233962306a36Sopenharmony_ci		}
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci		if (insn->type != INSN_JUMP_DYNAMIC &&
234262306a36Sopenharmony_ci		    insn->type != INSN_CALL_DYNAMIC &&
234362306a36Sopenharmony_ci		    insn->type != INSN_RETURN &&
234462306a36Sopenharmony_ci		    insn->type != INSN_NOP) {
234562306a36Sopenharmony_ci			WARN_INSN(insn, "retpoline_safe hint not an indirect jump/call/ret/nop");
234662306a36Sopenharmony_ci			return -1;
234762306a36Sopenharmony_ci		}
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_ci		insn->retpoline_safe = true;
235062306a36Sopenharmony_ci	}
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	return 0;
235362306a36Sopenharmony_ci}
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_cistatic int read_instr_hints(struct objtool_file *file)
235662306a36Sopenharmony_ci{
235762306a36Sopenharmony_ci	struct section *rsec;
235862306a36Sopenharmony_ci	struct instruction *insn;
235962306a36Sopenharmony_ci	struct reloc *reloc;
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	rsec = find_section_by_name(file->elf, ".rela.discard.instr_end");
236262306a36Sopenharmony_ci	if (!rsec)
236362306a36Sopenharmony_ci		return 0;
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	for_each_reloc(rsec, reloc) {
236662306a36Sopenharmony_ci		if (reloc->sym->type != STT_SECTION) {
236762306a36Sopenharmony_ci			WARN("unexpected relocation symbol type in %s", rsec->name);
236862306a36Sopenharmony_ci			return -1;
236962306a36Sopenharmony_ci		}
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci		insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
237262306a36Sopenharmony_ci		if (!insn) {
237362306a36Sopenharmony_ci			WARN("bad .discard.instr_end entry");
237462306a36Sopenharmony_ci			return -1;
237562306a36Sopenharmony_ci		}
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci		insn->instr--;
237862306a36Sopenharmony_ci	}
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	rsec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
238162306a36Sopenharmony_ci	if (!rsec)
238262306a36Sopenharmony_ci		return 0;
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ci	for_each_reloc(rsec, reloc) {
238562306a36Sopenharmony_ci		if (reloc->sym->type != STT_SECTION) {
238662306a36Sopenharmony_ci			WARN("unexpected relocation symbol type in %s", rsec->name);
238762306a36Sopenharmony_ci			return -1;
238862306a36Sopenharmony_ci		}
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci		insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
239162306a36Sopenharmony_ci		if (!insn) {
239262306a36Sopenharmony_ci			WARN("bad .discard.instr_begin entry");
239362306a36Sopenharmony_ci			return -1;
239462306a36Sopenharmony_ci		}
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_ci		insn->instr++;
239762306a36Sopenharmony_ci	}
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_ci	return 0;
240062306a36Sopenharmony_ci}
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_cistatic int read_validate_unret_hints(struct objtool_file *file)
240362306a36Sopenharmony_ci{
240462306a36Sopenharmony_ci	struct section *rsec;
240562306a36Sopenharmony_ci	struct instruction *insn;
240662306a36Sopenharmony_ci	struct reloc *reloc;
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci	rsec = find_section_by_name(file->elf, ".rela.discard.validate_unret");
240962306a36Sopenharmony_ci	if (!rsec)
241062306a36Sopenharmony_ci		return 0;
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_ci	for_each_reloc(rsec, reloc) {
241362306a36Sopenharmony_ci		if (reloc->sym->type != STT_SECTION) {
241462306a36Sopenharmony_ci			WARN("unexpected relocation symbol type in %s", rsec->name);
241562306a36Sopenharmony_ci			return -1;
241662306a36Sopenharmony_ci		}
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci		insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
241962306a36Sopenharmony_ci		if (!insn) {
242062306a36Sopenharmony_ci			WARN("bad .discard.instr_end entry");
242162306a36Sopenharmony_ci			return -1;
242262306a36Sopenharmony_ci		}
242362306a36Sopenharmony_ci		insn->unret = 1;
242462306a36Sopenharmony_ci	}
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ci	return 0;
242762306a36Sopenharmony_ci}
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_cistatic int read_intra_function_calls(struct objtool_file *file)
243162306a36Sopenharmony_ci{
243262306a36Sopenharmony_ci	struct instruction *insn;
243362306a36Sopenharmony_ci	struct section *rsec;
243462306a36Sopenharmony_ci	struct reloc *reloc;
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci	rsec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls");
243762306a36Sopenharmony_ci	if (!rsec)
243862306a36Sopenharmony_ci		return 0;
243962306a36Sopenharmony_ci
244062306a36Sopenharmony_ci	for_each_reloc(rsec, reloc) {
244162306a36Sopenharmony_ci		unsigned long dest_off;
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci		if (reloc->sym->type != STT_SECTION) {
244462306a36Sopenharmony_ci			WARN("unexpected relocation symbol type in %s",
244562306a36Sopenharmony_ci			     rsec->name);
244662306a36Sopenharmony_ci			return -1;
244762306a36Sopenharmony_ci		}
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci		insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
245062306a36Sopenharmony_ci		if (!insn) {
245162306a36Sopenharmony_ci			WARN("bad .discard.intra_function_call entry");
245262306a36Sopenharmony_ci			return -1;
245362306a36Sopenharmony_ci		}
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci		if (insn->type != INSN_CALL) {
245662306a36Sopenharmony_ci			WARN_INSN(insn, "intra_function_call not a direct call");
245762306a36Sopenharmony_ci			return -1;
245862306a36Sopenharmony_ci		}
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci		/*
246162306a36Sopenharmony_ci		 * Treat intra-function CALLs as JMPs, but with a stack_op.
246262306a36Sopenharmony_ci		 * See add_call_destinations(), which strips stack_ops from
246362306a36Sopenharmony_ci		 * normal CALLs.
246462306a36Sopenharmony_ci		 */
246562306a36Sopenharmony_ci		insn->type = INSN_JUMP_UNCONDITIONAL;
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ci		dest_off = arch_jump_destination(insn);
246862306a36Sopenharmony_ci		insn->jump_dest = find_insn(file, insn->sec, dest_off);
246962306a36Sopenharmony_ci		if (!insn->jump_dest) {
247062306a36Sopenharmony_ci			WARN_INSN(insn, "can't find call dest at %s+0x%lx",
247162306a36Sopenharmony_ci				  insn->sec->name, dest_off);
247262306a36Sopenharmony_ci			return -1;
247362306a36Sopenharmony_ci		}
247462306a36Sopenharmony_ci	}
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci	return 0;
247762306a36Sopenharmony_ci}
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci/*
248062306a36Sopenharmony_ci * Return true if name matches an instrumentation function, where calls to that
248162306a36Sopenharmony_ci * function from noinstr code can safely be removed, but compilers won't do so.
248262306a36Sopenharmony_ci */
248362306a36Sopenharmony_cistatic bool is_profiling_func(const char *name)
248462306a36Sopenharmony_ci{
248562306a36Sopenharmony_ci	/*
248662306a36Sopenharmony_ci	 * Many compilers cannot disable KCOV with a function attribute.
248762306a36Sopenharmony_ci	 */
248862306a36Sopenharmony_ci	if (!strncmp(name, "__sanitizer_cov_", 16))
248962306a36Sopenharmony_ci		return true;
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	/*
249262306a36Sopenharmony_ci	 * Some compilers currently do not remove __tsan_func_entry/exit nor
249362306a36Sopenharmony_ci	 * __tsan_atomic_signal_fence (used for barrier instrumentation) with
249462306a36Sopenharmony_ci	 * the __no_sanitize_thread attribute, remove them. Once the kernel's
249562306a36Sopenharmony_ci	 * minimum Clang version is 14.0, this can be removed.
249662306a36Sopenharmony_ci	 */
249762306a36Sopenharmony_ci	if (!strncmp(name, "__tsan_func_", 12) ||
249862306a36Sopenharmony_ci	    !strcmp(name, "__tsan_atomic_signal_fence"))
249962306a36Sopenharmony_ci		return true;
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci	return false;
250262306a36Sopenharmony_ci}
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_cistatic int classify_symbols(struct objtool_file *file)
250562306a36Sopenharmony_ci{
250662306a36Sopenharmony_ci	struct symbol *func;
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci	for_each_sym(file, func) {
250962306a36Sopenharmony_ci		if (func->bind != STB_GLOBAL)
251062306a36Sopenharmony_ci			continue;
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_ci		if (!strncmp(func->name, STATIC_CALL_TRAMP_PREFIX_STR,
251362306a36Sopenharmony_ci			     strlen(STATIC_CALL_TRAMP_PREFIX_STR)))
251462306a36Sopenharmony_ci			func->static_call_tramp = true;
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci		if (arch_is_retpoline(func))
251762306a36Sopenharmony_ci			func->retpoline_thunk = true;
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci		if (arch_is_rethunk(func))
252062306a36Sopenharmony_ci			func->return_thunk = true;
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci		if (arch_is_embedded_insn(func))
252362306a36Sopenharmony_ci			func->embedded_insn = true;
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_ci		if (arch_ftrace_match(func->name))
252662306a36Sopenharmony_ci			func->fentry = true;
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci		if (is_profiling_func(func->name))
252962306a36Sopenharmony_ci			func->profiling_func = true;
253062306a36Sopenharmony_ci	}
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ci	return 0;
253362306a36Sopenharmony_ci}
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_cistatic void mark_rodata(struct objtool_file *file)
253662306a36Sopenharmony_ci{
253762306a36Sopenharmony_ci	struct section *sec;
253862306a36Sopenharmony_ci	bool found = false;
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_ci	/*
254162306a36Sopenharmony_ci	 * Search for the following rodata sections, each of which can
254262306a36Sopenharmony_ci	 * potentially contain jump tables:
254362306a36Sopenharmony_ci	 *
254462306a36Sopenharmony_ci	 * - .rodata: can contain GCC switch tables
254562306a36Sopenharmony_ci	 * - .rodata.<func>: same, if -fdata-sections is being used
254662306a36Sopenharmony_ci	 * - .rodata..c_jump_table: contains C annotated jump tables
254762306a36Sopenharmony_ci	 *
254862306a36Sopenharmony_ci	 * .rodata.str1.* sections are ignored; they don't contain jump tables.
254962306a36Sopenharmony_ci	 */
255062306a36Sopenharmony_ci	for_each_sec(file, sec) {
255162306a36Sopenharmony_ci		if (!strncmp(sec->name, ".rodata", 7) &&
255262306a36Sopenharmony_ci		    !strstr(sec->name, ".str1.")) {
255362306a36Sopenharmony_ci			sec->rodata = true;
255462306a36Sopenharmony_ci			found = true;
255562306a36Sopenharmony_ci		}
255662306a36Sopenharmony_ci	}
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci	file->rodata = found;
255962306a36Sopenharmony_ci}
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_cistatic int decode_sections(struct objtool_file *file)
256262306a36Sopenharmony_ci{
256362306a36Sopenharmony_ci	int ret;
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	mark_rodata(file);
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_ci	ret = init_pv_ops(file);
256862306a36Sopenharmony_ci	if (ret)
256962306a36Sopenharmony_ci		return ret;
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci	/*
257262306a36Sopenharmony_ci	 * Must be before add_{jump_call}_destination.
257362306a36Sopenharmony_ci	 */
257462306a36Sopenharmony_ci	ret = classify_symbols(file);
257562306a36Sopenharmony_ci	if (ret)
257662306a36Sopenharmony_ci		return ret;
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	ret = decode_instructions(file);
257962306a36Sopenharmony_ci	if (ret)
258062306a36Sopenharmony_ci		return ret;
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	add_ignores(file);
258362306a36Sopenharmony_ci	add_uaccess_safe(file);
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci	ret = add_ignore_alternatives(file);
258662306a36Sopenharmony_ci	if (ret)
258762306a36Sopenharmony_ci		return ret;
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_ci	/*
259062306a36Sopenharmony_ci	 * Must be before read_unwind_hints() since that needs insn->noendbr.
259162306a36Sopenharmony_ci	 */
259262306a36Sopenharmony_ci	ret = read_noendbr_hints(file);
259362306a36Sopenharmony_ci	if (ret)
259462306a36Sopenharmony_ci		return ret;
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci	/*
259762306a36Sopenharmony_ci	 * Must be before add_jump_destinations(), which depends on 'func'
259862306a36Sopenharmony_ci	 * being set for alternatives, to enable proper sibling call detection.
259962306a36Sopenharmony_ci	 */
260062306a36Sopenharmony_ci	if (opts.stackval || opts.orc || opts.uaccess || opts.noinstr) {
260162306a36Sopenharmony_ci		ret = add_special_section_alts(file);
260262306a36Sopenharmony_ci		if (ret)
260362306a36Sopenharmony_ci			return ret;
260462306a36Sopenharmony_ci	}
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_ci	ret = add_jump_destinations(file);
260762306a36Sopenharmony_ci	if (ret)
260862306a36Sopenharmony_ci		return ret;
260962306a36Sopenharmony_ci
261062306a36Sopenharmony_ci	/*
261162306a36Sopenharmony_ci	 * Must be before add_call_destination(); it changes INSN_CALL to
261262306a36Sopenharmony_ci	 * INSN_JUMP.
261362306a36Sopenharmony_ci	 */
261462306a36Sopenharmony_ci	ret = read_intra_function_calls(file);
261562306a36Sopenharmony_ci	if (ret)
261662306a36Sopenharmony_ci		return ret;
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci	ret = add_call_destinations(file);
261962306a36Sopenharmony_ci	if (ret)
262062306a36Sopenharmony_ci		return ret;
262162306a36Sopenharmony_ci
262262306a36Sopenharmony_ci	/*
262362306a36Sopenharmony_ci	 * Must be after add_call_destinations() such that it can override
262462306a36Sopenharmony_ci	 * dead_end_function() marks.
262562306a36Sopenharmony_ci	 */
262662306a36Sopenharmony_ci	ret = add_dead_ends(file);
262762306a36Sopenharmony_ci	if (ret)
262862306a36Sopenharmony_ci		return ret;
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci	ret = add_jump_table_alts(file);
263162306a36Sopenharmony_ci	if (ret)
263262306a36Sopenharmony_ci		return ret;
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	ret = read_unwind_hints(file);
263562306a36Sopenharmony_ci	if (ret)
263662306a36Sopenharmony_ci		return ret;
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_ci	ret = read_retpoline_hints(file);
263962306a36Sopenharmony_ci	if (ret)
264062306a36Sopenharmony_ci		return ret;
264162306a36Sopenharmony_ci
264262306a36Sopenharmony_ci	ret = read_instr_hints(file);
264362306a36Sopenharmony_ci	if (ret)
264462306a36Sopenharmony_ci		return ret;
264562306a36Sopenharmony_ci
264662306a36Sopenharmony_ci	ret = read_validate_unret_hints(file);
264762306a36Sopenharmony_ci	if (ret)
264862306a36Sopenharmony_ci		return ret;
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_ci	return 0;
265162306a36Sopenharmony_ci}
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_cistatic bool is_special_call(struct instruction *insn)
265462306a36Sopenharmony_ci{
265562306a36Sopenharmony_ci	if (insn->type == INSN_CALL) {
265662306a36Sopenharmony_ci		struct symbol *dest = insn_call_dest(insn);
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ci		if (!dest)
265962306a36Sopenharmony_ci			return false;
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_ci		if (dest->fentry || dest->embedded_insn)
266262306a36Sopenharmony_ci			return true;
266362306a36Sopenharmony_ci	}
266462306a36Sopenharmony_ci
266562306a36Sopenharmony_ci	return false;
266662306a36Sopenharmony_ci}
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_cistatic bool has_modified_stack_frame(struct instruction *insn, struct insn_state *state)
266962306a36Sopenharmony_ci{
267062306a36Sopenharmony_ci	struct cfi_state *cfi = &state->cfi;
267162306a36Sopenharmony_ci	int i;
267262306a36Sopenharmony_ci
267362306a36Sopenharmony_ci	if (cfi->cfa.base != initial_func_cfi.cfa.base || cfi->drap)
267462306a36Sopenharmony_ci		return true;
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci	if (cfi->cfa.offset != initial_func_cfi.cfa.offset)
267762306a36Sopenharmony_ci		return true;
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci	if (cfi->stack_size != initial_func_cfi.cfa.offset)
268062306a36Sopenharmony_ci		return true;
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci	for (i = 0; i < CFI_NUM_REGS; i++) {
268362306a36Sopenharmony_ci		if (cfi->regs[i].base != initial_func_cfi.regs[i].base ||
268462306a36Sopenharmony_ci		    cfi->regs[i].offset != initial_func_cfi.regs[i].offset)
268562306a36Sopenharmony_ci			return true;
268662306a36Sopenharmony_ci	}
268762306a36Sopenharmony_ci
268862306a36Sopenharmony_ci	return false;
268962306a36Sopenharmony_ci}
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_cistatic bool check_reg_frame_pos(const struct cfi_reg *reg,
269262306a36Sopenharmony_ci				int expected_offset)
269362306a36Sopenharmony_ci{
269462306a36Sopenharmony_ci	return reg->base == CFI_CFA &&
269562306a36Sopenharmony_ci	       reg->offset == expected_offset;
269662306a36Sopenharmony_ci}
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_cistatic bool has_valid_stack_frame(struct insn_state *state)
269962306a36Sopenharmony_ci{
270062306a36Sopenharmony_ci	struct cfi_state *cfi = &state->cfi;
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_ci	if (cfi->cfa.base == CFI_BP &&
270362306a36Sopenharmony_ci	    check_reg_frame_pos(&cfi->regs[CFI_BP], -cfi->cfa.offset) &&
270462306a36Sopenharmony_ci	    check_reg_frame_pos(&cfi->regs[CFI_RA], -cfi->cfa.offset + 8))
270562306a36Sopenharmony_ci		return true;
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci	if (cfi->drap && cfi->regs[CFI_BP].base == CFI_BP)
270862306a36Sopenharmony_ci		return true;
270962306a36Sopenharmony_ci
271062306a36Sopenharmony_ci	return false;
271162306a36Sopenharmony_ci}
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_cistatic int update_cfi_state_regs(struct instruction *insn,
271462306a36Sopenharmony_ci				  struct cfi_state *cfi,
271562306a36Sopenharmony_ci				  struct stack_op *op)
271662306a36Sopenharmony_ci{
271762306a36Sopenharmony_ci	struct cfi_reg *cfa = &cfi->cfa;
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_ci	if (cfa->base != CFI_SP && cfa->base != CFI_SP_INDIRECT)
272062306a36Sopenharmony_ci		return 0;
272162306a36Sopenharmony_ci
272262306a36Sopenharmony_ci	/* push */
272362306a36Sopenharmony_ci	if (op->dest.type == OP_DEST_PUSH || op->dest.type == OP_DEST_PUSHF)
272462306a36Sopenharmony_ci		cfa->offset += 8;
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci	/* pop */
272762306a36Sopenharmony_ci	if (op->src.type == OP_SRC_POP || op->src.type == OP_SRC_POPF)
272862306a36Sopenharmony_ci		cfa->offset -= 8;
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci	/* add immediate to sp */
273162306a36Sopenharmony_ci	if (op->dest.type == OP_DEST_REG && op->src.type == OP_SRC_ADD &&
273262306a36Sopenharmony_ci	    op->dest.reg == CFI_SP && op->src.reg == CFI_SP)
273362306a36Sopenharmony_ci		cfa->offset -= op->src.offset;
273462306a36Sopenharmony_ci
273562306a36Sopenharmony_ci	return 0;
273662306a36Sopenharmony_ci}
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_cistatic void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int offset)
273962306a36Sopenharmony_ci{
274062306a36Sopenharmony_ci	if (arch_callee_saved_reg(reg) &&
274162306a36Sopenharmony_ci	    cfi->regs[reg].base == CFI_UNDEFINED) {
274262306a36Sopenharmony_ci		cfi->regs[reg].base = base;
274362306a36Sopenharmony_ci		cfi->regs[reg].offset = offset;
274462306a36Sopenharmony_ci	}
274562306a36Sopenharmony_ci}
274662306a36Sopenharmony_ci
274762306a36Sopenharmony_cistatic void restore_reg(struct cfi_state *cfi, unsigned char reg)
274862306a36Sopenharmony_ci{
274962306a36Sopenharmony_ci	cfi->regs[reg].base = initial_func_cfi.regs[reg].base;
275062306a36Sopenharmony_ci	cfi->regs[reg].offset = initial_func_cfi.regs[reg].offset;
275162306a36Sopenharmony_ci}
275262306a36Sopenharmony_ci
275362306a36Sopenharmony_ci/*
275462306a36Sopenharmony_ci * A note about DRAP stack alignment:
275562306a36Sopenharmony_ci *
275662306a36Sopenharmony_ci * GCC has the concept of a DRAP register, which is used to help keep track of
275762306a36Sopenharmony_ci * the stack pointer when aligning the stack.  r10 or r13 is used as the DRAP
275862306a36Sopenharmony_ci * register.  The typical DRAP pattern is:
275962306a36Sopenharmony_ci *
276062306a36Sopenharmony_ci *   4c 8d 54 24 08		lea    0x8(%rsp),%r10
276162306a36Sopenharmony_ci *   48 83 e4 c0		and    $0xffffffffffffffc0,%rsp
276262306a36Sopenharmony_ci *   41 ff 72 f8		pushq  -0x8(%r10)
276362306a36Sopenharmony_ci *   55				push   %rbp
276462306a36Sopenharmony_ci *   48 89 e5			mov    %rsp,%rbp
276562306a36Sopenharmony_ci *				(more pushes)
276662306a36Sopenharmony_ci *   41 52			push   %r10
276762306a36Sopenharmony_ci *				...
276862306a36Sopenharmony_ci *   41 5a			pop    %r10
276962306a36Sopenharmony_ci *				(more pops)
277062306a36Sopenharmony_ci *   5d				pop    %rbp
277162306a36Sopenharmony_ci *   49 8d 62 f8		lea    -0x8(%r10),%rsp
277262306a36Sopenharmony_ci *   c3				retq
277362306a36Sopenharmony_ci *
277462306a36Sopenharmony_ci * There are some variations in the epilogues, like:
277562306a36Sopenharmony_ci *
277662306a36Sopenharmony_ci *   5b				pop    %rbx
277762306a36Sopenharmony_ci *   41 5a			pop    %r10
277862306a36Sopenharmony_ci *   41 5c			pop    %r12
277962306a36Sopenharmony_ci *   41 5d			pop    %r13
278062306a36Sopenharmony_ci *   41 5e			pop    %r14
278162306a36Sopenharmony_ci *   c9				leaveq
278262306a36Sopenharmony_ci *   49 8d 62 f8		lea    -0x8(%r10),%rsp
278362306a36Sopenharmony_ci *   c3				retq
278462306a36Sopenharmony_ci *
278562306a36Sopenharmony_ci * and:
278662306a36Sopenharmony_ci *
278762306a36Sopenharmony_ci *   4c 8b 55 e8		mov    -0x18(%rbp),%r10
278862306a36Sopenharmony_ci *   48 8b 5d e0		mov    -0x20(%rbp),%rbx
278962306a36Sopenharmony_ci *   4c 8b 65 f0		mov    -0x10(%rbp),%r12
279062306a36Sopenharmony_ci *   4c 8b 6d f8		mov    -0x8(%rbp),%r13
279162306a36Sopenharmony_ci *   c9				leaveq
279262306a36Sopenharmony_ci *   49 8d 62 f8		lea    -0x8(%r10),%rsp
279362306a36Sopenharmony_ci *   c3				retq
279462306a36Sopenharmony_ci *
279562306a36Sopenharmony_ci * Sometimes r13 is used as the DRAP register, in which case it's saved and
279662306a36Sopenharmony_ci * restored beforehand:
279762306a36Sopenharmony_ci *
279862306a36Sopenharmony_ci *   41 55			push   %r13
279962306a36Sopenharmony_ci *   4c 8d 6c 24 10		lea    0x10(%rsp),%r13
280062306a36Sopenharmony_ci *   48 83 e4 f0		and    $0xfffffffffffffff0,%rsp
280162306a36Sopenharmony_ci *				...
280262306a36Sopenharmony_ci *   49 8d 65 f0		lea    -0x10(%r13),%rsp
280362306a36Sopenharmony_ci *   41 5d			pop    %r13
280462306a36Sopenharmony_ci *   c3				retq
280562306a36Sopenharmony_ci */
280662306a36Sopenharmony_cistatic int update_cfi_state(struct instruction *insn,
280762306a36Sopenharmony_ci			    struct instruction *next_insn,
280862306a36Sopenharmony_ci			    struct cfi_state *cfi, struct stack_op *op)
280962306a36Sopenharmony_ci{
281062306a36Sopenharmony_ci	struct cfi_reg *cfa = &cfi->cfa;
281162306a36Sopenharmony_ci	struct cfi_reg *regs = cfi->regs;
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_ci	/* ignore UNWIND_HINT_UNDEFINED regions */
281462306a36Sopenharmony_ci	if (cfi->force_undefined)
281562306a36Sopenharmony_ci		return 0;
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	/* stack operations don't make sense with an undefined CFA */
281862306a36Sopenharmony_ci	if (cfa->base == CFI_UNDEFINED) {
281962306a36Sopenharmony_ci		if (insn_func(insn)) {
282062306a36Sopenharmony_ci			WARN_INSN(insn, "undefined stack state");
282162306a36Sopenharmony_ci			return -1;
282262306a36Sopenharmony_ci		}
282362306a36Sopenharmony_ci		return 0;
282462306a36Sopenharmony_ci	}
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_ci	if (cfi->type == UNWIND_HINT_TYPE_REGS ||
282762306a36Sopenharmony_ci	    cfi->type == UNWIND_HINT_TYPE_REGS_PARTIAL)
282862306a36Sopenharmony_ci		return update_cfi_state_regs(insn, cfi, op);
282962306a36Sopenharmony_ci
283062306a36Sopenharmony_ci	switch (op->dest.type) {
283162306a36Sopenharmony_ci
283262306a36Sopenharmony_ci	case OP_DEST_REG:
283362306a36Sopenharmony_ci		switch (op->src.type) {
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci		case OP_SRC_REG:
283662306a36Sopenharmony_ci			if (op->src.reg == CFI_SP && op->dest.reg == CFI_BP &&
283762306a36Sopenharmony_ci			    cfa->base == CFI_SP &&
283862306a36Sopenharmony_ci			    check_reg_frame_pos(&regs[CFI_BP], -cfa->offset)) {
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci				/* mov %rsp, %rbp */
284162306a36Sopenharmony_ci				cfa->base = op->dest.reg;
284262306a36Sopenharmony_ci				cfi->bp_scratch = false;
284362306a36Sopenharmony_ci			}
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ci			else if (op->src.reg == CFI_SP &&
284662306a36Sopenharmony_ci				 op->dest.reg == CFI_BP && cfi->drap) {
284762306a36Sopenharmony_ci
284862306a36Sopenharmony_ci				/* drap: mov %rsp, %rbp */
284962306a36Sopenharmony_ci				regs[CFI_BP].base = CFI_BP;
285062306a36Sopenharmony_ci				regs[CFI_BP].offset = -cfi->stack_size;
285162306a36Sopenharmony_ci				cfi->bp_scratch = false;
285262306a36Sopenharmony_ci			}
285362306a36Sopenharmony_ci
285462306a36Sopenharmony_ci			else if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
285562306a36Sopenharmony_ci
285662306a36Sopenharmony_ci				/*
285762306a36Sopenharmony_ci				 * mov %rsp, %reg
285862306a36Sopenharmony_ci				 *
285962306a36Sopenharmony_ci				 * This is needed for the rare case where GCC
286062306a36Sopenharmony_ci				 * does:
286162306a36Sopenharmony_ci				 *
286262306a36Sopenharmony_ci				 *   mov    %rsp, %rax
286362306a36Sopenharmony_ci				 *   ...
286462306a36Sopenharmony_ci				 *   mov    %rax, %rsp
286562306a36Sopenharmony_ci				 */
286662306a36Sopenharmony_ci				cfi->vals[op->dest.reg].base = CFI_CFA;
286762306a36Sopenharmony_ci				cfi->vals[op->dest.reg].offset = -cfi->stack_size;
286862306a36Sopenharmony_ci			}
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_ci			else if (op->src.reg == CFI_BP && op->dest.reg == CFI_SP &&
287162306a36Sopenharmony_ci				 (cfa->base == CFI_BP || cfa->base == cfi->drap_reg)) {
287262306a36Sopenharmony_ci
287362306a36Sopenharmony_ci				/*
287462306a36Sopenharmony_ci				 * mov %rbp, %rsp
287562306a36Sopenharmony_ci				 *
287662306a36Sopenharmony_ci				 * Restore the original stack pointer (Clang).
287762306a36Sopenharmony_ci				 */
287862306a36Sopenharmony_ci				cfi->stack_size = -cfi->regs[CFI_BP].offset;
287962306a36Sopenharmony_ci			}
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci			else if (op->dest.reg == cfa->base) {
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_ci				/* mov %reg, %rsp */
288462306a36Sopenharmony_ci				if (cfa->base == CFI_SP &&
288562306a36Sopenharmony_ci				    cfi->vals[op->src.reg].base == CFI_CFA) {
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_ci					/*
288862306a36Sopenharmony_ci					 * This is needed for the rare case
288962306a36Sopenharmony_ci					 * where GCC does something dumb like:
289062306a36Sopenharmony_ci					 *
289162306a36Sopenharmony_ci					 *   lea    0x8(%rsp), %rcx
289262306a36Sopenharmony_ci					 *   ...
289362306a36Sopenharmony_ci					 *   mov    %rcx, %rsp
289462306a36Sopenharmony_ci					 */
289562306a36Sopenharmony_ci					cfa->offset = -cfi->vals[op->src.reg].offset;
289662306a36Sopenharmony_ci					cfi->stack_size = cfa->offset;
289762306a36Sopenharmony_ci
289862306a36Sopenharmony_ci				} else if (cfa->base == CFI_SP &&
289962306a36Sopenharmony_ci					   cfi->vals[op->src.reg].base == CFI_SP_INDIRECT &&
290062306a36Sopenharmony_ci					   cfi->vals[op->src.reg].offset == cfa->offset) {
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_ci					/*
290362306a36Sopenharmony_ci					 * Stack swizzle:
290462306a36Sopenharmony_ci					 *
290562306a36Sopenharmony_ci					 * 1: mov %rsp, (%[tos])
290662306a36Sopenharmony_ci					 * 2: mov %[tos], %rsp
290762306a36Sopenharmony_ci					 *    ...
290862306a36Sopenharmony_ci					 * 3: pop %rsp
290962306a36Sopenharmony_ci					 *
291062306a36Sopenharmony_ci					 * Where:
291162306a36Sopenharmony_ci					 *
291262306a36Sopenharmony_ci					 * 1 - places a pointer to the previous
291362306a36Sopenharmony_ci					 *     stack at the Top-of-Stack of the
291462306a36Sopenharmony_ci					 *     new stack.
291562306a36Sopenharmony_ci					 *
291662306a36Sopenharmony_ci					 * 2 - switches to the new stack.
291762306a36Sopenharmony_ci					 *
291862306a36Sopenharmony_ci					 * 3 - pops the Top-of-Stack to restore
291962306a36Sopenharmony_ci					 *     the original stack.
292062306a36Sopenharmony_ci					 *
292162306a36Sopenharmony_ci					 * Note: we set base to SP_INDIRECT
292262306a36Sopenharmony_ci					 * here and preserve offset. Therefore
292362306a36Sopenharmony_ci					 * when the unwinder reaches ToS it
292462306a36Sopenharmony_ci					 * will dereference SP and then add the
292562306a36Sopenharmony_ci					 * offset to find the next frame, IOW:
292662306a36Sopenharmony_ci					 * (%rsp) + offset.
292762306a36Sopenharmony_ci					 */
292862306a36Sopenharmony_ci					cfa->base = CFI_SP_INDIRECT;
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci				} else {
293162306a36Sopenharmony_ci					cfa->base = CFI_UNDEFINED;
293262306a36Sopenharmony_ci					cfa->offset = 0;
293362306a36Sopenharmony_ci				}
293462306a36Sopenharmony_ci			}
293562306a36Sopenharmony_ci
293662306a36Sopenharmony_ci			else if (op->dest.reg == CFI_SP &&
293762306a36Sopenharmony_ci				 cfi->vals[op->src.reg].base == CFI_SP_INDIRECT &&
293862306a36Sopenharmony_ci				 cfi->vals[op->src.reg].offset == cfa->offset) {
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci				/*
294162306a36Sopenharmony_ci				 * The same stack swizzle case 2) as above. But
294262306a36Sopenharmony_ci				 * because we can't change cfa->base, case 3)
294362306a36Sopenharmony_ci				 * will become a regular POP. Pretend we're a
294462306a36Sopenharmony_ci				 * PUSH so things don't go unbalanced.
294562306a36Sopenharmony_ci				 */
294662306a36Sopenharmony_ci				cfi->stack_size += 8;
294762306a36Sopenharmony_ci			}
294862306a36Sopenharmony_ci
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci			break;
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_ci		case OP_SRC_ADD:
295362306a36Sopenharmony_ci			if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) {
295462306a36Sopenharmony_ci
295562306a36Sopenharmony_ci				/* add imm, %rsp */
295662306a36Sopenharmony_ci				cfi->stack_size -= op->src.offset;
295762306a36Sopenharmony_ci				if (cfa->base == CFI_SP)
295862306a36Sopenharmony_ci					cfa->offset -= op->src.offset;
295962306a36Sopenharmony_ci				break;
296062306a36Sopenharmony_ci			}
296162306a36Sopenharmony_ci
296262306a36Sopenharmony_ci			if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) {
296362306a36Sopenharmony_ci
296462306a36Sopenharmony_ci				/* lea disp(%rbp), %rsp */
296562306a36Sopenharmony_ci				cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset);
296662306a36Sopenharmony_ci				break;
296762306a36Sopenharmony_ci			}
296862306a36Sopenharmony_ci
296962306a36Sopenharmony_ci			if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_ci				/* drap: lea disp(%rsp), %drap */
297262306a36Sopenharmony_ci				cfi->drap_reg = op->dest.reg;
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci				/*
297562306a36Sopenharmony_ci				 * lea disp(%rsp), %reg
297662306a36Sopenharmony_ci				 *
297762306a36Sopenharmony_ci				 * This is needed for the rare case where GCC
297862306a36Sopenharmony_ci				 * does something dumb like:
297962306a36Sopenharmony_ci				 *
298062306a36Sopenharmony_ci				 *   lea    0x8(%rsp), %rcx
298162306a36Sopenharmony_ci				 *   ...
298262306a36Sopenharmony_ci				 *   mov    %rcx, %rsp
298362306a36Sopenharmony_ci				 */
298462306a36Sopenharmony_ci				cfi->vals[op->dest.reg].base = CFI_CFA;
298562306a36Sopenharmony_ci				cfi->vals[op->dest.reg].offset = \
298662306a36Sopenharmony_ci					-cfi->stack_size + op->src.offset;
298762306a36Sopenharmony_ci
298862306a36Sopenharmony_ci				break;
298962306a36Sopenharmony_ci			}
299062306a36Sopenharmony_ci
299162306a36Sopenharmony_ci			if (cfi->drap && op->dest.reg == CFI_SP &&
299262306a36Sopenharmony_ci			    op->src.reg == cfi->drap_reg) {
299362306a36Sopenharmony_ci
299462306a36Sopenharmony_ci				 /* drap: lea disp(%drap), %rsp */
299562306a36Sopenharmony_ci				cfa->base = CFI_SP;
299662306a36Sopenharmony_ci				cfa->offset = cfi->stack_size = -op->src.offset;
299762306a36Sopenharmony_ci				cfi->drap_reg = CFI_UNDEFINED;
299862306a36Sopenharmony_ci				cfi->drap = false;
299962306a36Sopenharmony_ci				break;
300062306a36Sopenharmony_ci			}
300162306a36Sopenharmony_ci
300262306a36Sopenharmony_ci			if (op->dest.reg == cfi->cfa.base && !(next_insn && next_insn->hint)) {
300362306a36Sopenharmony_ci				WARN_INSN(insn, "unsupported stack register modification");
300462306a36Sopenharmony_ci				return -1;
300562306a36Sopenharmony_ci			}
300662306a36Sopenharmony_ci
300762306a36Sopenharmony_ci			break;
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_ci		case OP_SRC_AND:
301062306a36Sopenharmony_ci			if (op->dest.reg != CFI_SP ||
301162306a36Sopenharmony_ci			    (cfi->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
301262306a36Sopenharmony_ci			    (cfi->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
301362306a36Sopenharmony_ci				WARN_INSN(insn, "unsupported stack pointer realignment");
301462306a36Sopenharmony_ci				return -1;
301562306a36Sopenharmony_ci			}
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_ci			if (cfi->drap_reg != CFI_UNDEFINED) {
301862306a36Sopenharmony_ci				/* drap: and imm, %rsp */
301962306a36Sopenharmony_ci				cfa->base = cfi->drap_reg;
302062306a36Sopenharmony_ci				cfa->offset = cfi->stack_size = 0;
302162306a36Sopenharmony_ci				cfi->drap = true;
302262306a36Sopenharmony_ci			}
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci			/*
302562306a36Sopenharmony_ci			 * Older versions of GCC (4.8ish) realign the stack
302662306a36Sopenharmony_ci			 * without DRAP, with a frame pointer.
302762306a36Sopenharmony_ci			 */
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_ci			break;
303062306a36Sopenharmony_ci
303162306a36Sopenharmony_ci		case OP_SRC_POP:
303262306a36Sopenharmony_ci		case OP_SRC_POPF:
303362306a36Sopenharmony_ci			if (op->dest.reg == CFI_SP && cfa->base == CFI_SP_INDIRECT) {
303462306a36Sopenharmony_ci
303562306a36Sopenharmony_ci				/* pop %rsp; # restore from a stack swizzle */
303662306a36Sopenharmony_ci				cfa->base = CFI_SP;
303762306a36Sopenharmony_ci				break;
303862306a36Sopenharmony_ci			}
303962306a36Sopenharmony_ci
304062306a36Sopenharmony_ci			if (!cfi->drap && op->dest.reg == cfa->base) {
304162306a36Sopenharmony_ci
304262306a36Sopenharmony_ci				/* pop %rbp */
304362306a36Sopenharmony_ci				cfa->base = CFI_SP;
304462306a36Sopenharmony_ci			}
304562306a36Sopenharmony_ci
304662306a36Sopenharmony_ci			if (cfi->drap && cfa->base == CFI_BP_INDIRECT &&
304762306a36Sopenharmony_ci			    op->dest.reg == cfi->drap_reg &&
304862306a36Sopenharmony_ci			    cfi->drap_offset == -cfi->stack_size) {
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci				/* drap: pop %drap */
305162306a36Sopenharmony_ci				cfa->base = cfi->drap_reg;
305262306a36Sopenharmony_ci				cfa->offset = 0;
305362306a36Sopenharmony_ci				cfi->drap_offset = -1;
305462306a36Sopenharmony_ci
305562306a36Sopenharmony_ci			} else if (cfi->stack_size == -regs[op->dest.reg].offset) {
305662306a36Sopenharmony_ci
305762306a36Sopenharmony_ci				/* pop %reg */
305862306a36Sopenharmony_ci				restore_reg(cfi, op->dest.reg);
305962306a36Sopenharmony_ci			}
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci			cfi->stack_size -= 8;
306262306a36Sopenharmony_ci			if (cfa->base == CFI_SP)
306362306a36Sopenharmony_ci				cfa->offset -= 8;
306462306a36Sopenharmony_ci
306562306a36Sopenharmony_ci			break;
306662306a36Sopenharmony_ci
306762306a36Sopenharmony_ci		case OP_SRC_REG_INDIRECT:
306862306a36Sopenharmony_ci			if (!cfi->drap && op->dest.reg == cfa->base &&
306962306a36Sopenharmony_ci			    op->dest.reg == CFI_BP) {
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ci				/* mov disp(%rsp), %rbp */
307262306a36Sopenharmony_ci				cfa->base = CFI_SP;
307362306a36Sopenharmony_ci				cfa->offset = cfi->stack_size;
307462306a36Sopenharmony_ci			}
307562306a36Sopenharmony_ci
307662306a36Sopenharmony_ci			if (cfi->drap && op->src.reg == CFI_BP &&
307762306a36Sopenharmony_ci			    op->src.offset == cfi->drap_offset) {
307862306a36Sopenharmony_ci
307962306a36Sopenharmony_ci				/* drap: mov disp(%rbp), %drap */
308062306a36Sopenharmony_ci				cfa->base = cfi->drap_reg;
308162306a36Sopenharmony_ci				cfa->offset = 0;
308262306a36Sopenharmony_ci				cfi->drap_offset = -1;
308362306a36Sopenharmony_ci			}
308462306a36Sopenharmony_ci
308562306a36Sopenharmony_ci			if (cfi->drap && op->src.reg == CFI_BP &&
308662306a36Sopenharmony_ci			    op->src.offset == regs[op->dest.reg].offset) {
308762306a36Sopenharmony_ci
308862306a36Sopenharmony_ci				/* drap: mov disp(%rbp), %reg */
308962306a36Sopenharmony_ci				restore_reg(cfi, op->dest.reg);
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ci			} else if (op->src.reg == cfa->base &&
309262306a36Sopenharmony_ci			    op->src.offset == regs[op->dest.reg].offset + cfa->offset) {
309362306a36Sopenharmony_ci
309462306a36Sopenharmony_ci				/* mov disp(%rbp), %reg */
309562306a36Sopenharmony_ci				/* mov disp(%rsp), %reg */
309662306a36Sopenharmony_ci				restore_reg(cfi, op->dest.reg);
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci			} else if (op->src.reg == CFI_SP &&
309962306a36Sopenharmony_ci				   op->src.offset == regs[op->dest.reg].offset + cfi->stack_size) {
310062306a36Sopenharmony_ci
310162306a36Sopenharmony_ci				/* mov disp(%rsp), %reg */
310262306a36Sopenharmony_ci				restore_reg(cfi, op->dest.reg);
310362306a36Sopenharmony_ci			}
310462306a36Sopenharmony_ci
310562306a36Sopenharmony_ci			break;
310662306a36Sopenharmony_ci
310762306a36Sopenharmony_ci		default:
310862306a36Sopenharmony_ci			WARN_INSN(insn, "unknown stack-related instruction");
310962306a36Sopenharmony_ci			return -1;
311062306a36Sopenharmony_ci		}
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_ci		break;
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_ci	case OP_DEST_PUSH:
311562306a36Sopenharmony_ci	case OP_DEST_PUSHF:
311662306a36Sopenharmony_ci		cfi->stack_size += 8;
311762306a36Sopenharmony_ci		if (cfa->base == CFI_SP)
311862306a36Sopenharmony_ci			cfa->offset += 8;
311962306a36Sopenharmony_ci
312062306a36Sopenharmony_ci		if (op->src.type != OP_SRC_REG)
312162306a36Sopenharmony_ci			break;
312262306a36Sopenharmony_ci
312362306a36Sopenharmony_ci		if (cfi->drap) {
312462306a36Sopenharmony_ci			if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
312562306a36Sopenharmony_ci
312662306a36Sopenharmony_ci				/* drap: push %drap */
312762306a36Sopenharmony_ci				cfa->base = CFI_BP_INDIRECT;
312862306a36Sopenharmony_ci				cfa->offset = -cfi->stack_size;
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci				/* save drap so we know when to restore it */
313162306a36Sopenharmony_ci				cfi->drap_offset = -cfi->stack_size;
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci			} else if (op->src.reg == CFI_BP && cfa->base == cfi->drap_reg) {
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ci				/* drap: push %rbp */
313662306a36Sopenharmony_ci				cfi->stack_size = 0;
313762306a36Sopenharmony_ci
313862306a36Sopenharmony_ci			} else {
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci				/* drap: push %reg */
314162306a36Sopenharmony_ci				save_reg(cfi, op->src.reg, CFI_BP, -cfi->stack_size);
314262306a36Sopenharmony_ci			}
314362306a36Sopenharmony_ci
314462306a36Sopenharmony_ci		} else {
314562306a36Sopenharmony_ci
314662306a36Sopenharmony_ci			/* push %reg */
314762306a36Sopenharmony_ci			save_reg(cfi, op->src.reg, CFI_CFA, -cfi->stack_size);
314862306a36Sopenharmony_ci		}
314962306a36Sopenharmony_ci
315062306a36Sopenharmony_ci		/* detect when asm code uses rbp as a scratch register */
315162306a36Sopenharmony_ci		if (opts.stackval && insn_func(insn) && op->src.reg == CFI_BP &&
315262306a36Sopenharmony_ci		    cfa->base != CFI_BP)
315362306a36Sopenharmony_ci			cfi->bp_scratch = true;
315462306a36Sopenharmony_ci		break;
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	case OP_DEST_REG_INDIRECT:
315762306a36Sopenharmony_ci
315862306a36Sopenharmony_ci		if (cfi->drap) {
315962306a36Sopenharmony_ci			if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ci				/* drap: mov %drap, disp(%rbp) */
316262306a36Sopenharmony_ci				cfa->base = CFI_BP_INDIRECT;
316362306a36Sopenharmony_ci				cfa->offset = op->dest.offset;
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_ci				/* save drap offset so we know when to restore it */
316662306a36Sopenharmony_ci				cfi->drap_offset = op->dest.offset;
316762306a36Sopenharmony_ci			} else {
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci				/* drap: mov reg, disp(%rbp) */
317062306a36Sopenharmony_ci				save_reg(cfi, op->src.reg, CFI_BP, op->dest.offset);
317162306a36Sopenharmony_ci			}
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci		} else if (op->dest.reg == cfa->base) {
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_ci			/* mov reg, disp(%rbp) */
317662306a36Sopenharmony_ci			/* mov reg, disp(%rsp) */
317762306a36Sopenharmony_ci			save_reg(cfi, op->src.reg, CFI_CFA,
317862306a36Sopenharmony_ci				 op->dest.offset - cfi->cfa.offset);
317962306a36Sopenharmony_ci
318062306a36Sopenharmony_ci		} else if (op->dest.reg == CFI_SP) {
318162306a36Sopenharmony_ci
318262306a36Sopenharmony_ci			/* mov reg, disp(%rsp) */
318362306a36Sopenharmony_ci			save_reg(cfi, op->src.reg, CFI_CFA,
318462306a36Sopenharmony_ci				 op->dest.offset - cfi->stack_size);
318562306a36Sopenharmony_ci
318662306a36Sopenharmony_ci		} else if (op->src.reg == CFI_SP && op->dest.offset == 0) {
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_ci			/* mov %rsp, (%reg); # setup a stack swizzle. */
318962306a36Sopenharmony_ci			cfi->vals[op->dest.reg].base = CFI_SP_INDIRECT;
319062306a36Sopenharmony_ci			cfi->vals[op->dest.reg].offset = cfa->offset;
319162306a36Sopenharmony_ci		}
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci		break;
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_ci	case OP_DEST_MEM:
319662306a36Sopenharmony_ci		if (op->src.type != OP_SRC_POP && op->src.type != OP_SRC_POPF) {
319762306a36Sopenharmony_ci			WARN_INSN(insn, "unknown stack-related memory operation");
319862306a36Sopenharmony_ci			return -1;
319962306a36Sopenharmony_ci		}
320062306a36Sopenharmony_ci
320162306a36Sopenharmony_ci		/* pop mem */
320262306a36Sopenharmony_ci		cfi->stack_size -= 8;
320362306a36Sopenharmony_ci		if (cfa->base == CFI_SP)
320462306a36Sopenharmony_ci			cfa->offset -= 8;
320562306a36Sopenharmony_ci
320662306a36Sopenharmony_ci		break;
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_ci	default:
320962306a36Sopenharmony_ci		WARN_INSN(insn, "unknown stack-related instruction");
321062306a36Sopenharmony_ci		return -1;
321162306a36Sopenharmony_ci	}
321262306a36Sopenharmony_ci
321362306a36Sopenharmony_ci	return 0;
321462306a36Sopenharmony_ci}
321562306a36Sopenharmony_ci
321662306a36Sopenharmony_ci/*
321762306a36Sopenharmony_ci * The stack layouts of alternatives instructions can sometimes diverge when
321862306a36Sopenharmony_ci * they have stack modifications.  That's fine as long as the potential stack
321962306a36Sopenharmony_ci * layouts don't conflict at any given potential instruction boundary.
322062306a36Sopenharmony_ci *
322162306a36Sopenharmony_ci * Flatten the CFIs of the different alternative code streams (both original
322262306a36Sopenharmony_ci * and replacement) into a single shared CFI array which can be used to detect
322362306a36Sopenharmony_ci * conflicts and nicely feed a linear array of ORC entries to the unwinder.
322462306a36Sopenharmony_ci */
322562306a36Sopenharmony_cistatic int propagate_alt_cfi(struct objtool_file *file, struct instruction *insn)
322662306a36Sopenharmony_ci{
322762306a36Sopenharmony_ci	struct cfi_state **alt_cfi;
322862306a36Sopenharmony_ci	int group_off;
322962306a36Sopenharmony_ci
323062306a36Sopenharmony_ci	if (!insn->alt_group)
323162306a36Sopenharmony_ci		return 0;
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci	if (!insn->cfi) {
323462306a36Sopenharmony_ci		WARN("CFI missing");
323562306a36Sopenharmony_ci		return -1;
323662306a36Sopenharmony_ci	}
323762306a36Sopenharmony_ci
323862306a36Sopenharmony_ci	alt_cfi = insn->alt_group->cfi;
323962306a36Sopenharmony_ci	group_off = insn->offset - insn->alt_group->first_insn->offset;
324062306a36Sopenharmony_ci
324162306a36Sopenharmony_ci	if (!alt_cfi[group_off]) {
324262306a36Sopenharmony_ci		alt_cfi[group_off] = insn->cfi;
324362306a36Sopenharmony_ci	} else {
324462306a36Sopenharmony_ci		if (cficmp(alt_cfi[group_off], insn->cfi)) {
324562306a36Sopenharmony_ci			struct alt_group *orig_group = insn->alt_group->orig_group ?: insn->alt_group;
324662306a36Sopenharmony_ci			struct instruction *orig = orig_group->first_insn;
324762306a36Sopenharmony_ci			char *where = offstr(insn->sec, insn->offset);
324862306a36Sopenharmony_ci			WARN_INSN(orig, "stack layout conflict in alternatives: %s", where);
324962306a36Sopenharmony_ci			free(where);
325062306a36Sopenharmony_ci			return -1;
325162306a36Sopenharmony_ci		}
325262306a36Sopenharmony_ci	}
325362306a36Sopenharmony_ci
325462306a36Sopenharmony_ci	return 0;
325562306a36Sopenharmony_ci}
325662306a36Sopenharmony_ci
325762306a36Sopenharmony_cistatic int handle_insn_ops(struct instruction *insn,
325862306a36Sopenharmony_ci			   struct instruction *next_insn,
325962306a36Sopenharmony_ci			   struct insn_state *state)
326062306a36Sopenharmony_ci{
326162306a36Sopenharmony_ci	struct stack_op *op;
326262306a36Sopenharmony_ci
326362306a36Sopenharmony_ci	for (op = insn->stack_ops; op; op = op->next) {
326462306a36Sopenharmony_ci
326562306a36Sopenharmony_ci		if (update_cfi_state(insn, next_insn, &state->cfi, op))
326662306a36Sopenharmony_ci			return 1;
326762306a36Sopenharmony_ci
326862306a36Sopenharmony_ci		if (!insn->alt_group)
326962306a36Sopenharmony_ci			continue;
327062306a36Sopenharmony_ci
327162306a36Sopenharmony_ci		if (op->dest.type == OP_DEST_PUSHF) {
327262306a36Sopenharmony_ci			if (!state->uaccess_stack) {
327362306a36Sopenharmony_ci				state->uaccess_stack = 1;
327462306a36Sopenharmony_ci			} else if (state->uaccess_stack >> 31) {
327562306a36Sopenharmony_ci				WARN_INSN(insn, "PUSHF stack exhausted");
327662306a36Sopenharmony_ci				return 1;
327762306a36Sopenharmony_ci			}
327862306a36Sopenharmony_ci			state->uaccess_stack <<= 1;
327962306a36Sopenharmony_ci			state->uaccess_stack  |= state->uaccess;
328062306a36Sopenharmony_ci		}
328162306a36Sopenharmony_ci
328262306a36Sopenharmony_ci		if (op->src.type == OP_SRC_POPF) {
328362306a36Sopenharmony_ci			if (state->uaccess_stack) {
328462306a36Sopenharmony_ci				state->uaccess = state->uaccess_stack & 1;
328562306a36Sopenharmony_ci				state->uaccess_stack >>= 1;
328662306a36Sopenharmony_ci				if (state->uaccess_stack == 1)
328762306a36Sopenharmony_ci					state->uaccess_stack = 0;
328862306a36Sopenharmony_ci			}
328962306a36Sopenharmony_ci		}
329062306a36Sopenharmony_ci	}
329162306a36Sopenharmony_ci
329262306a36Sopenharmony_ci	return 0;
329362306a36Sopenharmony_ci}
329462306a36Sopenharmony_ci
329562306a36Sopenharmony_cistatic bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2)
329662306a36Sopenharmony_ci{
329762306a36Sopenharmony_ci	struct cfi_state *cfi1 = insn->cfi;
329862306a36Sopenharmony_ci	int i;
329962306a36Sopenharmony_ci
330062306a36Sopenharmony_ci	if (!cfi1) {
330162306a36Sopenharmony_ci		WARN("CFI missing");
330262306a36Sopenharmony_ci		return false;
330362306a36Sopenharmony_ci	}
330462306a36Sopenharmony_ci
330562306a36Sopenharmony_ci	if (memcmp(&cfi1->cfa, &cfi2->cfa, sizeof(cfi1->cfa))) {
330662306a36Sopenharmony_ci
330762306a36Sopenharmony_ci		WARN_INSN(insn, "stack state mismatch: cfa1=%d%+d cfa2=%d%+d",
330862306a36Sopenharmony_ci			  cfi1->cfa.base, cfi1->cfa.offset,
330962306a36Sopenharmony_ci			  cfi2->cfa.base, cfi2->cfa.offset);
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_ci	} else if (memcmp(&cfi1->regs, &cfi2->regs, sizeof(cfi1->regs))) {
331262306a36Sopenharmony_ci		for (i = 0; i < CFI_NUM_REGS; i++) {
331362306a36Sopenharmony_ci			if (!memcmp(&cfi1->regs[i], &cfi2->regs[i],
331462306a36Sopenharmony_ci				    sizeof(struct cfi_reg)))
331562306a36Sopenharmony_ci				continue;
331662306a36Sopenharmony_ci
331762306a36Sopenharmony_ci			WARN_INSN(insn, "stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d",
331862306a36Sopenharmony_ci				  i, cfi1->regs[i].base, cfi1->regs[i].offset,
331962306a36Sopenharmony_ci				  i, cfi2->regs[i].base, cfi2->regs[i].offset);
332062306a36Sopenharmony_ci			break;
332162306a36Sopenharmony_ci		}
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci	} else if (cfi1->type != cfi2->type) {
332462306a36Sopenharmony_ci
332562306a36Sopenharmony_ci		WARN_INSN(insn, "stack state mismatch: type1=%d type2=%d",
332662306a36Sopenharmony_ci			  cfi1->type, cfi2->type);
332762306a36Sopenharmony_ci
332862306a36Sopenharmony_ci	} else if (cfi1->drap != cfi2->drap ||
332962306a36Sopenharmony_ci		   (cfi1->drap && cfi1->drap_reg != cfi2->drap_reg) ||
333062306a36Sopenharmony_ci		   (cfi1->drap && cfi1->drap_offset != cfi2->drap_offset)) {
333162306a36Sopenharmony_ci
333262306a36Sopenharmony_ci		WARN_INSN(insn, "stack state mismatch: drap1=%d(%d,%d) drap2=%d(%d,%d)",
333362306a36Sopenharmony_ci			  cfi1->drap, cfi1->drap_reg, cfi1->drap_offset,
333462306a36Sopenharmony_ci			  cfi2->drap, cfi2->drap_reg, cfi2->drap_offset);
333562306a36Sopenharmony_ci
333662306a36Sopenharmony_ci	} else
333762306a36Sopenharmony_ci		return true;
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci	return false;
334062306a36Sopenharmony_ci}
334162306a36Sopenharmony_ci
334262306a36Sopenharmony_cistatic inline bool func_uaccess_safe(struct symbol *func)
334362306a36Sopenharmony_ci{
334462306a36Sopenharmony_ci	if (func)
334562306a36Sopenharmony_ci		return func->uaccess_safe;
334662306a36Sopenharmony_ci
334762306a36Sopenharmony_ci	return false;
334862306a36Sopenharmony_ci}
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_cistatic inline const char *call_dest_name(struct instruction *insn)
335162306a36Sopenharmony_ci{
335262306a36Sopenharmony_ci	static char pvname[19];
335362306a36Sopenharmony_ci	struct reloc *reloc;
335462306a36Sopenharmony_ci	int idx;
335562306a36Sopenharmony_ci
335662306a36Sopenharmony_ci	if (insn_call_dest(insn))
335762306a36Sopenharmony_ci		return insn_call_dest(insn)->name;
335862306a36Sopenharmony_ci
335962306a36Sopenharmony_ci	reloc = insn_reloc(NULL, insn);
336062306a36Sopenharmony_ci	if (reloc && !strcmp(reloc->sym->name, "pv_ops")) {
336162306a36Sopenharmony_ci		idx = (reloc_addend(reloc) / sizeof(void *));
336262306a36Sopenharmony_ci		snprintf(pvname, sizeof(pvname), "pv_ops[%d]", idx);
336362306a36Sopenharmony_ci		return pvname;
336462306a36Sopenharmony_ci	}
336562306a36Sopenharmony_ci
336662306a36Sopenharmony_ci	return "{dynamic}";
336762306a36Sopenharmony_ci}
336862306a36Sopenharmony_ci
336962306a36Sopenharmony_cistatic bool pv_call_dest(struct objtool_file *file, struct instruction *insn)
337062306a36Sopenharmony_ci{
337162306a36Sopenharmony_ci	struct symbol *target;
337262306a36Sopenharmony_ci	struct reloc *reloc;
337362306a36Sopenharmony_ci	int idx;
337462306a36Sopenharmony_ci
337562306a36Sopenharmony_ci	reloc = insn_reloc(file, insn);
337662306a36Sopenharmony_ci	if (!reloc || strcmp(reloc->sym->name, "pv_ops"))
337762306a36Sopenharmony_ci		return false;
337862306a36Sopenharmony_ci
337962306a36Sopenharmony_ci	idx = (arch_dest_reloc_offset(reloc_addend(reloc)) / sizeof(void *));
338062306a36Sopenharmony_ci
338162306a36Sopenharmony_ci	if (file->pv_ops[idx].clean)
338262306a36Sopenharmony_ci		return true;
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_ci	file->pv_ops[idx].clean = true;
338562306a36Sopenharmony_ci
338662306a36Sopenharmony_ci	list_for_each_entry(target, &file->pv_ops[idx].targets, pv_target) {
338762306a36Sopenharmony_ci		if (!target->sec->noinstr) {
338862306a36Sopenharmony_ci			WARN("pv_ops[%d]: %s", idx, target->name);
338962306a36Sopenharmony_ci			file->pv_ops[idx].clean = false;
339062306a36Sopenharmony_ci		}
339162306a36Sopenharmony_ci	}
339262306a36Sopenharmony_ci
339362306a36Sopenharmony_ci	return file->pv_ops[idx].clean;
339462306a36Sopenharmony_ci}
339562306a36Sopenharmony_ci
339662306a36Sopenharmony_cistatic inline bool noinstr_call_dest(struct objtool_file *file,
339762306a36Sopenharmony_ci				     struct instruction *insn,
339862306a36Sopenharmony_ci				     struct symbol *func)
339962306a36Sopenharmony_ci{
340062306a36Sopenharmony_ci	/*
340162306a36Sopenharmony_ci	 * We can't deal with indirect function calls at present;
340262306a36Sopenharmony_ci	 * assume they're instrumented.
340362306a36Sopenharmony_ci	 */
340462306a36Sopenharmony_ci	if (!func) {
340562306a36Sopenharmony_ci		if (file->pv_ops)
340662306a36Sopenharmony_ci			return pv_call_dest(file, insn);
340762306a36Sopenharmony_ci
340862306a36Sopenharmony_ci		return false;
340962306a36Sopenharmony_ci	}
341062306a36Sopenharmony_ci
341162306a36Sopenharmony_ci	/*
341262306a36Sopenharmony_ci	 * If the symbol is from a noinstr section; we good.
341362306a36Sopenharmony_ci	 */
341462306a36Sopenharmony_ci	if (func->sec->noinstr)
341562306a36Sopenharmony_ci		return true;
341662306a36Sopenharmony_ci
341762306a36Sopenharmony_ci	/*
341862306a36Sopenharmony_ci	 * If the symbol is a static_call trampoline, we can't tell.
341962306a36Sopenharmony_ci	 */
342062306a36Sopenharmony_ci	if (func->static_call_tramp)
342162306a36Sopenharmony_ci		return true;
342262306a36Sopenharmony_ci
342362306a36Sopenharmony_ci	/*
342462306a36Sopenharmony_ci	 * The __ubsan_handle_*() calls are like WARN(), they only happen when
342562306a36Sopenharmony_ci	 * something 'BAD' happened. At the risk of taking the machine down,
342662306a36Sopenharmony_ci	 * let them proceed to get the message out.
342762306a36Sopenharmony_ci	 */
342862306a36Sopenharmony_ci	if (!strncmp(func->name, "__ubsan_handle_", 15))
342962306a36Sopenharmony_ci		return true;
343062306a36Sopenharmony_ci
343162306a36Sopenharmony_ci	return false;
343262306a36Sopenharmony_ci}
343362306a36Sopenharmony_ci
343462306a36Sopenharmony_cistatic int validate_call(struct objtool_file *file,
343562306a36Sopenharmony_ci			 struct instruction *insn,
343662306a36Sopenharmony_ci			 struct insn_state *state)
343762306a36Sopenharmony_ci{
343862306a36Sopenharmony_ci	if (state->noinstr && state->instr <= 0 &&
343962306a36Sopenharmony_ci	    !noinstr_call_dest(file, insn, insn_call_dest(insn))) {
344062306a36Sopenharmony_ci		WARN_INSN(insn, "call to %s() leaves .noinstr.text section", call_dest_name(insn));
344162306a36Sopenharmony_ci		return 1;
344262306a36Sopenharmony_ci	}
344362306a36Sopenharmony_ci
344462306a36Sopenharmony_ci	if (state->uaccess && !func_uaccess_safe(insn_call_dest(insn))) {
344562306a36Sopenharmony_ci		WARN_INSN(insn, "call to %s() with UACCESS enabled", call_dest_name(insn));
344662306a36Sopenharmony_ci		return 1;
344762306a36Sopenharmony_ci	}
344862306a36Sopenharmony_ci
344962306a36Sopenharmony_ci	if (state->df) {
345062306a36Sopenharmony_ci		WARN_INSN(insn, "call to %s() with DF set", call_dest_name(insn));
345162306a36Sopenharmony_ci		return 1;
345262306a36Sopenharmony_ci	}
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci	return 0;
345562306a36Sopenharmony_ci}
345662306a36Sopenharmony_ci
345762306a36Sopenharmony_cistatic int validate_sibling_call(struct objtool_file *file,
345862306a36Sopenharmony_ci				 struct instruction *insn,
345962306a36Sopenharmony_ci				 struct insn_state *state)
346062306a36Sopenharmony_ci{
346162306a36Sopenharmony_ci	if (insn_func(insn) && has_modified_stack_frame(insn, state)) {
346262306a36Sopenharmony_ci		WARN_INSN(insn, "sibling call from callable instruction with modified stack frame");
346362306a36Sopenharmony_ci		return 1;
346462306a36Sopenharmony_ci	}
346562306a36Sopenharmony_ci
346662306a36Sopenharmony_ci	return validate_call(file, insn, state);
346762306a36Sopenharmony_ci}
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_cistatic int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
347062306a36Sopenharmony_ci{
347162306a36Sopenharmony_ci	if (state->noinstr && state->instr > 0) {
347262306a36Sopenharmony_ci		WARN_INSN(insn, "return with instrumentation enabled");
347362306a36Sopenharmony_ci		return 1;
347462306a36Sopenharmony_ci	}
347562306a36Sopenharmony_ci
347662306a36Sopenharmony_ci	if (state->uaccess && !func_uaccess_safe(func)) {
347762306a36Sopenharmony_ci		WARN_INSN(insn, "return with UACCESS enabled");
347862306a36Sopenharmony_ci		return 1;
347962306a36Sopenharmony_ci	}
348062306a36Sopenharmony_ci
348162306a36Sopenharmony_ci	if (!state->uaccess && func_uaccess_safe(func)) {
348262306a36Sopenharmony_ci		WARN_INSN(insn, "return with UACCESS disabled from a UACCESS-safe function");
348362306a36Sopenharmony_ci		return 1;
348462306a36Sopenharmony_ci	}
348562306a36Sopenharmony_ci
348662306a36Sopenharmony_ci	if (state->df) {
348762306a36Sopenharmony_ci		WARN_INSN(insn, "return with DF set");
348862306a36Sopenharmony_ci		return 1;
348962306a36Sopenharmony_ci	}
349062306a36Sopenharmony_ci
349162306a36Sopenharmony_ci	if (func && has_modified_stack_frame(insn, state)) {
349262306a36Sopenharmony_ci		WARN_INSN(insn, "return with modified stack frame");
349362306a36Sopenharmony_ci		return 1;
349462306a36Sopenharmony_ci	}
349562306a36Sopenharmony_ci
349662306a36Sopenharmony_ci	if (state->cfi.bp_scratch) {
349762306a36Sopenharmony_ci		WARN_INSN(insn, "BP used as a scratch register");
349862306a36Sopenharmony_ci		return 1;
349962306a36Sopenharmony_ci	}
350062306a36Sopenharmony_ci
350162306a36Sopenharmony_ci	return 0;
350262306a36Sopenharmony_ci}
350362306a36Sopenharmony_ci
350462306a36Sopenharmony_cistatic struct instruction *next_insn_to_validate(struct objtool_file *file,
350562306a36Sopenharmony_ci						 struct instruction *insn)
350662306a36Sopenharmony_ci{
350762306a36Sopenharmony_ci	struct alt_group *alt_group = insn->alt_group;
350862306a36Sopenharmony_ci
350962306a36Sopenharmony_ci	/*
351062306a36Sopenharmony_ci	 * Simulate the fact that alternatives are patched in-place.  When the
351162306a36Sopenharmony_ci	 * end of a replacement alt_group is reached, redirect objtool flow to
351262306a36Sopenharmony_ci	 * the end of the original alt_group.
351362306a36Sopenharmony_ci	 *
351462306a36Sopenharmony_ci	 * insn->alts->insn -> alt_group->first_insn
351562306a36Sopenharmony_ci	 *		       ...
351662306a36Sopenharmony_ci	 *		       alt_group->last_insn
351762306a36Sopenharmony_ci	 *		       [alt_group->nop]      -> next(orig_group->last_insn)
351862306a36Sopenharmony_ci	 */
351962306a36Sopenharmony_ci	if (alt_group) {
352062306a36Sopenharmony_ci		if (alt_group->nop) {
352162306a36Sopenharmony_ci			/* ->nop implies ->orig_group */
352262306a36Sopenharmony_ci			if (insn == alt_group->last_insn)
352362306a36Sopenharmony_ci				return alt_group->nop;
352462306a36Sopenharmony_ci			if (insn == alt_group->nop)
352562306a36Sopenharmony_ci				goto next_orig;
352662306a36Sopenharmony_ci		}
352762306a36Sopenharmony_ci		if (insn == alt_group->last_insn && alt_group->orig_group)
352862306a36Sopenharmony_ci			goto next_orig;
352962306a36Sopenharmony_ci	}
353062306a36Sopenharmony_ci
353162306a36Sopenharmony_ci	return next_insn_same_sec(file, insn);
353262306a36Sopenharmony_ci
353362306a36Sopenharmony_cinext_orig:
353462306a36Sopenharmony_ci	return next_insn_same_sec(file, alt_group->orig_group->last_insn);
353562306a36Sopenharmony_ci}
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_ci/*
353862306a36Sopenharmony_ci * Follow the branch starting at the given instruction, and recursively follow
353962306a36Sopenharmony_ci * any other branches (jumps).  Meanwhile, track the frame pointer state at
354062306a36Sopenharmony_ci * each instruction and validate all the rules described in
354162306a36Sopenharmony_ci * tools/objtool/Documentation/objtool.txt.
354262306a36Sopenharmony_ci */
354362306a36Sopenharmony_cistatic int validate_branch(struct objtool_file *file, struct symbol *func,
354462306a36Sopenharmony_ci			   struct instruction *insn, struct insn_state state)
354562306a36Sopenharmony_ci{
354662306a36Sopenharmony_ci	struct alternative *alt;
354762306a36Sopenharmony_ci	struct instruction *next_insn, *prev_insn = NULL;
354862306a36Sopenharmony_ci	struct section *sec;
354962306a36Sopenharmony_ci	u8 visited;
355062306a36Sopenharmony_ci	int ret;
355162306a36Sopenharmony_ci
355262306a36Sopenharmony_ci	sec = insn->sec;
355362306a36Sopenharmony_ci
355462306a36Sopenharmony_ci	while (1) {
355562306a36Sopenharmony_ci		next_insn = next_insn_to_validate(file, insn);
355662306a36Sopenharmony_ci
355762306a36Sopenharmony_ci		if (func && insn_func(insn) && func != insn_func(insn)->pfunc) {
355862306a36Sopenharmony_ci			/* Ignore KCFI type preambles, which always fall through */
355962306a36Sopenharmony_ci			if (!strncmp(func->name, "__cfi_", 6) ||
356062306a36Sopenharmony_ci			    !strncmp(func->name, "__pfx_", 6))
356162306a36Sopenharmony_ci				return 0;
356262306a36Sopenharmony_ci
356362306a36Sopenharmony_ci			WARN("%s() falls through to next function %s()",
356462306a36Sopenharmony_ci			     func->name, insn_func(insn)->name);
356562306a36Sopenharmony_ci			return 1;
356662306a36Sopenharmony_ci		}
356762306a36Sopenharmony_ci
356862306a36Sopenharmony_ci		if (func && insn->ignore) {
356962306a36Sopenharmony_ci			WARN_INSN(insn, "BUG: why am I validating an ignored function?");
357062306a36Sopenharmony_ci			return 1;
357162306a36Sopenharmony_ci		}
357262306a36Sopenharmony_ci
357362306a36Sopenharmony_ci		visited = VISITED_BRANCH << state.uaccess;
357462306a36Sopenharmony_ci		if (insn->visited & VISITED_BRANCH_MASK) {
357562306a36Sopenharmony_ci			if (!insn->hint && !insn_cfi_match(insn, &state.cfi))
357662306a36Sopenharmony_ci				return 1;
357762306a36Sopenharmony_ci
357862306a36Sopenharmony_ci			if (insn->visited & visited)
357962306a36Sopenharmony_ci				return 0;
358062306a36Sopenharmony_ci		} else {
358162306a36Sopenharmony_ci			nr_insns_visited++;
358262306a36Sopenharmony_ci		}
358362306a36Sopenharmony_ci
358462306a36Sopenharmony_ci		if (state.noinstr)
358562306a36Sopenharmony_ci			state.instr += insn->instr;
358662306a36Sopenharmony_ci
358762306a36Sopenharmony_ci		if (insn->hint) {
358862306a36Sopenharmony_ci			if (insn->restore) {
358962306a36Sopenharmony_ci				struct instruction *save_insn, *i;
359062306a36Sopenharmony_ci
359162306a36Sopenharmony_ci				i = insn;
359262306a36Sopenharmony_ci				save_insn = NULL;
359362306a36Sopenharmony_ci
359462306a36Sopenharmony_ci				sym_for_each_insn_continue_reverse(file, func, i) {
359562306a36Sopenharmony_ci					if (i->save) {
359662306a36Sopenharmony_ci						save_insn = i;
359762306a36Sopenharmony_ci						break;
359862306a36Sopenharmony_ci					}
359962306a36Sopenharmony_ci				}
360062306a36Sopenharmony_ci
360162306a36Sopenharmony_ci				if (!save_insn) {
360262306a36Sopenharmony_ci					WARN_INSN(insn, "no corresponding CFI save for CFI restore");
360362306a36Sopenharmony_ci					return 1;
360462306a36Sopenharmony_ci				}
360562306a36Sopenharmony_ci
360662306a36Sopenharmony_ci				if (!save_insn->visited) {
360762306a36Sopenharmony_ci					/*
360862306a36Sopenharmony_ci					 * If the restore hint insn is at the
360962306a36Sopenharmony_ci					 * beginning of a basic block and was
361062306a36Sopenharmony_ci					 * branched to from elsewhere, and the
361162306a36Sopenharmony_ci					 * save insn hasn't been visited yet,
361262306a36Sopenharmony_ci					 * defer following this branch for now.
361362306a36Sopenharmony_ci					 * It will be seen later via the
361462306a36Sopenharmony_ci					 * straight-line path.
361562306a36Sopenharmony_ci					 */
361662306a36Sopenharmony_ci					if (!prev_insn)
361762306a36Sopenharmony_ci						return 0;
361862306a36Sopenharmony_ci
361962306a36Sopenharmony_ci					WARN_INSN(insn, "objtool isn't smart enough to handle this CFI save/restore combo");
362062306a36Sopenharmony_ci					return 1;
362162306a36Sopenharmony_ci				}
362262306a36Sopenharmony_ci
362362306a36Sopenharmony_ci				insn->cfi = save_insn->cfi;
362462306a36Sopenharmony_ci				nr_cfi_reused++;
362562306a36Sopenharmony_ci			}
362662306a36Sopenharmony_ci
362762306a36Sopenharmony_ci			state.cfi = *insn->cfi;
362862306a36Sopenharmony_ci		} else {
362962306a36Sopenharmony_ci			/* XXX track if we actually changed state.cfi */
363062306a36Sopenharmony_ci
363162306a36Sopenharmony_ci			if (prev_insn && !cficmp(prev_insn->cfi, &state.cfi)) {
363262306a36Sopenharmony_ci				insn->cfi = prev_insn->cfi;
363362306a36Sopenharmony_ci				nr_cfi_reused++;
363462306a36Sopenharmony_ci			} else {
363562306a36Sopenharmony_ci				insn->cfi = cfi_hash_find_or_add(&state.cfi);
363662306a36Sopenharmony_ci			}
363762306a36Sopenharmony_ci		}
363862306a36Sopenharmony_ci
363962306a36Sopenharmony_ci		insn->visited |= visited;
364062306a36Sopenharmony_ci
364162306a36Sopenharmony_ci		if (propagate_alt_cfi(file, insn))
364262306a36Sopenharmony_ci			return 1;
364362306a36Sopenharmony_ci
364462306a36Sopenharmony_ci		if (!insn->ignore_alts && insn->alts) {
364562306a36Sopenharmony_ci			bool skip_orig = false;
364662306a36Sopenharmony_ci
364762306a36Sopenharmony_ci			for (alt = insn->alts; alt; alt = alt->next) {
364862306a36Sopenharmony_ci				if (alt->skip_orig)
364962306a36Sopenharmony_ci					skip_orig = true;
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_ci				ret = validate_branch(file, func, alt->insn, state);
365262306a36Sopenharmony_ci				if (ret) {
365362306a36Sopenharmony_ci					BT_INSN(insn, "(alt)");
365462306a36Sopenharmony_ci					return ret;
365562306a36Sopenharmony_ci				}
365662306a36Sopenharmony_ci			}
365762306a36Sopenharmony_ci
365862306a36Sopenharmony_ci			if (skip_orig)
365962306a36Sopenharmony_ci				return 0;
366062306a36Sopenharmony_ci		}
366162306a36Sopenharmony_ci
366262306a36Sopenharmony_ci		if (handle_insn_ops(insn, next_insn, &state))
366362306a36Sopenharmony_ci			return 1;
366462306a36Sopenharmony_ci
366562306a36Sopenharmony_ci		switch (insn->type) {
366662306a36Sopenharmony_ci
366762306a36Sopenharmony_ci		case INSN_RETURN:
366862306a36Sopenharmony_ci			return validate_return(func, insn, &state);
366962306a36Sopenharmony_ci
367062306a36Sopenharmony_ci		case INSN_CALL:
367162306a36Sopenharmony_ci		case INSN_CALL_DYNAMIC:
367262306a36Sopenharmony_ci			ret = validate_call(file, insn, &state);
367362306a36Sopenharmony_ci			if (ret)
367462306a36Sopenharmony_ci				return ret;
367562306a36Sopenharmony_ci
367662306a36Sopenharmony_ci			if (opts.stackval && func && !is_special_call(insn) &&
367762306a36Sopenharmony_ci			    !has_valid_stack_frame(&state)) {
367862306a36Sopenharmony_ci				WARN_INSN(insn, "call without frame pointer save/setup");
367962306a36Sopenharmony_ci				return 1;
368062306a36Sopenharmony_ci			}
368162306a36Sopenharmony_ci
368262306a36Sopenharmony_ci			if (insn->dead_end)
368362306a36Sopenharmony_ci				return 0;
368462306a36Sopenharmony_ci
368562306a36Sopenharmony_ci			break;
368662306a36Sopenharmony_ci
368762306a36Sopenharmony_ci		case INSN_JUMP_CONDITIONAL:
368862306a36Sopenharmony_ci		case INSN_JUMP_UNCONDITIONAL:
368962306a36Sopenharmony_ci			if (is_sibling_call(insn)) {
369062306a36Sopenharmony_ci				ret = validate_sibling_call(file, insn, &state);
369162306a36Sopenharmony_ci				if (ret)
369262306a36Sopenharmony_ci					return ret;
369362306a36Sopenharmony_ci
369462306a36Sopenharmony_ci			} else if (insn->jump_dest) {
369562306a36Sopenharmony_ci				ret = validate_branch(file, func,
369662306a36Sopenharmony_ci						      insn->jump_dest, state);
369762306a36Sopenharmony_ci				if (ret) {
369862306a36Sopenharmony_ci					BT_INSN(insn, "(branch)");
369962306a36Sopenharmony_ci					return ret;
370062306a36Sopenharmony_ci				}
370162306a36Sopenharmony_ci			}
370262306a36Sopenharmony_ci
370362306a36Sopenharmony_ci			if (insn->type == INSN_JUMP_UNCONDITIONAL)
370462306a36Sopenharmony_ci				return 0;
370562306a36Sopenharmony_ci
370662306a36Sopenharmony_ci			break;
370762306a36Sopenharmony_ci
370862306a36Sopenharmony_ci		case INSN_JUMP_DYNAMIC:
370962306a36Sopenharmony_ci		case INSN_JUMP_DYNAMIC_CONDITIONAL:
371062306a36Sopenharmony_ci			if (is_sibling_call(insn)) {
371162306a36Sopenharmony_ci				ret = validate_sibling_call(file, insn, &state);
371262306a36Sopenharmony_ci				if (ret)
371362306a36Sopenharmony_ci					return ret;
371462306a36Sopenharmony_ci			}
371562306a36Sopenharmony_ci
371662306a36Sopenharmony_ci			if (insn->type == INSN_JUMP_DYNAMIC)
371762306a36Sopenharmony_ci				return 0;
371862306a36Sopenharmony_ci
371962306a36Sopenharmony_ci			break;
372062306a36Sopenharmony_ci
372162306a36Sopenharmony_ci		case INSN_CONTEXT_SWITCH:
372262306a36Sopenharmony_ci			if (func && (!next_insn || !next_insn->hint)) {
372362306a36Sopenharmony_ci				WARN_INSN(insn, "unsupported instruction in callable function");
372462306a36Sopenharmony_ci				return 1;
372562306a36Sopenharmony_ci			}
372662306a36Sopenharmony_ci			return 0;
372762306a36Sopenharmony_ci
372862306a36Sopenharmony_ci		case INSN_STAC:
372962306a36Sopenharmony_ci			if (state.uaccess) {
373062306a36Sopenharmony_ci				WARN_INSN(insn, "recursive UACCESS enable");
373162306a36Sopenharmony_ci				return 1;
373262306a36Sopenharmony_ci			}
373362306a36Sopenharmony_ci
373462306a36Sopenharmony_ci			state.uaccess = true;
373562306a36Sopenharmony_ci			break;
373662306a36Sopenharmony_ci
373762306a36Sopenharmony_ci		case INSN_CLAC:
373862306a36Sopenharmony_ci			if (!state.uaccess && func) {
373962306a36Sopenharmony_ci				WARN_INSN(insn, "redundant UACCESS disable");
374062306a36Sopenharmony_ci				return 1;
374162306a36Sopenharmony_ci			}
374262306a36Sopenharmony_ci
374362306a36Sopenharmony_ci			if (func_uaccess_safe(func) && !state.uaccess_stack) {
374462306a36Sopenharmony_ci				WARN_INSN(insn, "UACCESS-safe disables UACCESS");
374562306a36Sopenharmony_ci				return 1;
374662306a36Sopenharmony_ci			}
374762306a36Sopenharmony_ci
374862306a36Sopenharmony_ci			state.uaccess = false;
374962306a36Sopenharmony_ci			break;
375062306a36Sopenharmony_ci
375162306a36Sopenharmony_ci		case INSN_STD:
375262306a36Sopenharmony_ci			if (state.df) {
375362306a36Sopenharmony_ci				WARN_INSN(insn, "recursive STD");
375462306a36Sopenharmony_ci				return 1;
375562306a36Sopenharmony_ci			}
375662306a36Sopenharmony_ci
375762306a36Sopenharmony_ci			state.df = true;
375862306a36Sopenharmony_ci			break;
375962306a36Sopenharmony_ci
376062306a36Sopenharmony_ci		case INSN_CLD:
376162306a36Sopenharmony_ci			if (!state.df && func) {
376262306a36Sopenharmony_ci				WARN_INSN(insn, "redundant CLD");
376362306a36Sopenharmony_ci				return 1;
376462306a36Sopenharmony_ci			}
376562306a36Sopenharmony_ci
376662306a36Sopenharmony_ci			state.df = false;
376762306a36Sopenharmony_ci			break;
376862306a36Sopenharmony_ci
376962306a36Sopenharmony_ci		default:
377062306a36Sopenharmony_ci			break;
377162306a36Sopenharmony_ci		}
377262306a36Sopenharmony_ci
377362306a36Sopenharmony_ci		if (insn->dead_end)
377462306a36Sopenharmony_ci			return 0;
377562306a36Sopenharmony_ci
377662306a36Sopenharmony_ci		if (!next_insn) {
377762306a36Sopenharmony_ci			if (state.cfi.cfa.base == CFI_UNDEFINED)
377862306a36Sopenharmony_ci				return 0;
377962306a36Sopenharmony_ci			WARN("%s: unexpected end of section", sec->name);
378062306a36Sopenharmony_ci			return 1;
378162306a36Sopenharmony_ci		}
378262306a36Sopenharmony_ci
378362306a36Sopenharmony_ci		prev_insn = insn;
378462306a36Sopenharmony_ci		insn = next_insn;
378562306a36Sopenharmony_ci	}
378662306a36Sopenharmony_ci
378762306a36Sopenharmony_ci	return 0;
378862306a36Sopenharmony_ci}
378962306a36Sopenharmony_ci
379062306a36Sopenharmony_cistatic int validate_unwind_hint(struct objtool_file *file,
379162306a36Sopenharmony_ci				  struct instruction *insn,
379262306a36Sopenharmony_ci				  struct insn_state *state)
379362306a36Sopenharmony_ci{
379462306a36Sopenharmony_ci	if (insn->hint && !insn->visited && !insn->ignore) {
379562306a36Sopenharmony_ci		int ret = validate_branch(file, insn_func(insn), insn, *state);
379662306a36Sopenharmony_ci		if (ret)
379762306a36Sopenharmony_ci			BT_INSN(insn, "<=== (hint)");
379862306a36Sopenharmony_ci		return ret;
379962306a36Sopenharmony_ci	}
380062306a36Sopenharmony_ci
380162306a36Sopenharmony_ci	return 0;
380262306a36Sopenharmony_ci}
380362306a36Sopenharmony_ci
380462306a36Sopenharmony_cistatic int validate_unwind_hints(struct objtool_file *file, struct section *sec)
380562306a36Sopenharmony_ci{
380662306a36Sopenharmony_ci	struct instruction *insn;
380762306a36Sopenharmony_ci	struct insn_state state;
380862306a36Sopenharmony_ci	int warnings = 0;
380962306a36Sopenharmony_ci
381062306a36Sopenharmony_ci	if (!file->hints)
381162306a36Sopenharmony_ci		return 0;
381262306a36Sopenharmony_ci
381362306a36Sopenharmony_ci	init_insn_state(file, &state, sec);
381462306a36Sopenharmony_ci
381562306a36Sopenharmony_ci	if (sec) {
381662306a36Sopenharmony_ci		sec_for_each_insn(file, sec, insn)
381762306a36Sopenharmony_ci			warnings += validate_unwind_hint(file, insn, &state);
381862306a36Sopenharmony_ci	} else {
381962306a36Sopenharmony_ci		for_each_insn(file, insn)
382062306a36Sopenharmony_ci			warnings += validate_unwind_hint(file, insn, &state);
382162306a36Sopenharmony_ci	}
382262306a36Sopenharmony_ci
382362306a36Sopenharmony_ci	return warnings;
382462306a36Sopenharmony_ci}
382562306a36Sopenharmony_ci
382662306a36Sopenharmony_ci/*
382762306a36Sopenharmony_ci * Validate rethunk entry constraint: must untrain RET before the first RET.
382862306a36Sopenharmony_ci *
382962306a36Sopenharmony_ci * Follow every branch (intra-function) and ensure VALIDATE_UNRET_END comes
383062306a36Sopenharmony_ci * before an actual RET instruction.
383162306a36Sopenharmony_ci */
383262306a36Sopenharmony_cistatic int validate_unret(struct objtool_file *file, struct instruction *insn)
383362306a36Sopenharmony_ci{
383462306a36Sopenharmony_ci	struct instruction *next, *dest;
383562306a36Sopenharmony_ci	int ret;
383662306a36Sopenharmony_ci
383762306a36Sopenharmony_ci	for (;;) {
383862306a36Sopenharmony_ci		next = next_insn_to_validate(file, insn);
383962306a36Sopenharmony_ci
384062306a36Sopenharmony_ci		if (insn->visited & VISITED_UNRET)
384162306a36Sopenharmony_ci			return 0;
384262306a36Sopenharmony_ci
384362306a36Sopenharmony_ci		insn->visited |= VISITED_UNRET;
384462306a36Sopenharmony_ci
384562306a36Sopenharmony_ci		if (!insn->ignore_alts && insn->alts) {
384662306a36Sopenharmony_ci			struct alternative *alt;
384762306a36Sopenharmony_ci			bool skip_orig = false;
384862306a36Sopenharmony_ci
384962306a36Sopenharmony_ci			for (alt = insn->alts; alt; alt = alt->next) {
385062306a36Sopenharmony_ci				if (alt->skip_orig)
385162306a36Sopenharmony_ci					skip_orig = true;
385262306a36Sopenharmony_ci
385362306a36Sopenharmony_ci				ret = validate_unret(file, alt->insn);
385462306a36Sopenharmony_ci				if (ret) {
385562306a36Sopenharmony_ci					BT_INSN(insn, "(alt)");
385662306a36Sopenharmony_ci					return ret;
385762306a36Sopenharmony_ci				}
385862306a36Sopenharmony_ci			}
385962306a36Sopenharmony_ci
386062306a36Sopenharmony_ci			if (skip_orig)
386162306a36Sopenharmony_ci				return 0;
386262306a36Sopenharmony_ci		}
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_ci		switch (insn->type) {
386562306a36Sopenharmony_ci
386662306a36Sopenharmony_ci		case INSN_CALL_DYNAMIC:
386762306a36Sopenharmony_ci		case INSN_JUMP_DYNAMIC:
386862306a36Sopenharmony_ci		case INSN_JUMP_DYNAMIC_CONDITIONAL:
386962306a36Sopenharmony_ci			WARN_INSN(insn, "early indirect call");
387062306a36Sopenharmony_ci			return 1;
387162306a36Sopenharmony_ci
387262306a36Sopenharmony_ci		case INSN_JUMP_UNCONDITIONAL:
387362306a36Sopenharmony_ci		case INSN_JUMP_CONDITIONAL:
387462306a36Sopenharmony_ci			if (!is_sibling_call(insn)) {
387562306a36Sopenharmony_ci				if (!insn->jump_dest) {
387662306a36Sopenharmony_ci					WARN_INSN(insn, "unresolved jump target after linking?!?");
387762306a36Sopenharmony_ci					return -1;
387862306a36Sopenharmony_ci				}
387962306a36Sopenharmony_ci				ret = validate_unret(file, insn->jump_dest);
388062306a36Sopenharmony_ci				if (ret) {
388162306a36Sopenharmony_ci					BT_INSN(insn, "(branch%s)",
388262306a36Sopenharmony_ci						insn->type == INSN_JUMP_CONDITIONAL ? "-cond" : "");
388362306a36Sopenharmony_ci					return ret;
388462306a36Sopenharmony_ci				}
388562306a36Sopenharmony_ci
388662306a36Sopenharmony_ci				if (insn->type == INSN_JUMP_UNCONDITIONAL)
388762306a36Sopenharmony_ci					return 0;
388862306a36Sopenharmony_ci
388962306a36Sopenharmony_ci				break;
389062306a36Sopenharmony_ci			}
389162306a36Sopenharmony_ci
389262306a36Sopenharmony_ci			/* fallthrough */
389362306a36Sopenharmony_ci		case INSN_CALL:
389462306a36Sopenharmony_ci			dest = find_insn(file, insn_call_dest(insn)->sec,
389562306a36Sopenharmony_ci					 insn_call_dest(insn)->offset);
389662306a36Sopenharmony_ci			if (!dest) {
389762306a36Sopenharmony_ci				WARN("Unresolved function after linking!?: %s",
389862306a36Sopenharmony_ci				     insn_call_dest(insn)->name);
389962306a36Sopenharmony_ci				return -1;
390062306a36Sopenharmony_ci			}
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_ci			ret = validate_unret(file, dest);
390362306a36Sopenharmony_ci			if (ret) {
390462306a36Sopenharmony_ci				BT_INSN(insn, "(call)");
390562306a36Sopenharmony_ci				return ret;
390662306a36Sopenharmony_ci			}
390762306a36Sopenharmony_ci			/*
390862306a36Sopenharmony_ci			 * If a call returns without error, it must have seen UNTRAIN_RET.
390962306a36Sopenharmony_ci			 * Therefore any non-error return is a success.
391062306a36Sopenharmony_ci			 */
391162306a36Sopenharmony_ci			return 0;
391262306a36Sopenharmony_ci
391362306a36Sopenharmony_ci		case INSN_RETURN:
391462306a36Sopenharmony_ci			WARN_INSN(insn, "RET before UNTRAIN");
391562306a36Sopenharmony_ci			return 1;
391662306a36Sopenharmony_ci
391762306a36Sopenharmony_ci		case INSN_NOP:
391862306a36Sopenharmony_ci			if (insn->retpoline_safe)
391962306a36Sopenharmony_ci				return 0;
392062306a36Sopenharmony_ci			break;
392162306a36Sopenharmony_ci
392262306a36Sopenharmony_ci		default:
392362306a36Sopenharmony_ci			break;
392462306a36Sopenharmony_ci		}
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ci		if (!next) {
392762306a36Sopenharmony_ci			WARN_INSN(insn, "teh end!");
392862306a36Sopenharmony_ci			return -1;
392962306a36Sopenharmony_ci		}
393062306a36Sopenharmony_ci		insn = next;
393162306a36Sopenharmony_ci	}
393262306a36Sopenharmony_ci
393362306a36Sopenharmony_ci	return 0;
393462306a36Sopenharmony_ci}
393562306a36Sopenharmony_ci
393662306a36Sopenharmony_ci/*
393762306a36Sopenharmony_ci * Validate that all branches starting at VALIDATE_UNRET_BEGIN encounter
393862306a36Sopenharmony_ci * VALIDATE_UNRET_END before RET.
393962306a36Sopenharmony_ci */
394062306a36Sopenharmony_cistatic int validate_unrets(struct objtool_file *file)
394162306a36Sopenharmony_ci{
394262306a36Sopenharmony_ci	struct instruction *insn;
394362306a36Sopenharmony_ci	int ret, warnings = 0;
394462306a36Sopenharmony_ci
394562306a36Sopenharmony_ci	for_each_insn(file, insn) {
394662306a36Sopenharmony_ci		if (!insn->unret)
394762306a36Sopenharmony_ci			continue;
394862306a36Sopenharmony_ci
394962306a36Sopenharmony_ci		ret = validate_unret(file, insn);
395062306a36Sopenharmony_ci		if (ret < 0) {
395162306a36Sopenharmony_ci			WARN_INSN(insn, "Failed UNRET validation");
395262306a36Sopenharmony_ci			return ret;
395362306a36Sopenharmony_ci		}
395462306a36Sopenharmony_ci		warnings += ret;
395562306a36Sopenharmony_ci	}
395662306a36Sopenharmony_ci
395762306a36Sopenharmony_ci	return warnings;
395862306a36Sopenharmony_ci}
395962306a36Sopenharmony_ci
396062306a36Sopenharmony_cistatic int validate_retpoline(struct objtool_file *file)
396162306a36Sopenharmony_ci{
396262306a36Sopenharmony_ci	struct instruction *insn;
396362306a36Sopenharmony_ci	int warnings = 0;
396462306a36Sopenharmony_ci
396562306a36Sopenharmony_ci	for_each_insn(file, insn) {
396662306a36Sopenharmony_ci		if (insn->type != INSN_JUMP_DYNAMIC &&
396762306a36Sopenharmony_ci		    insn->type != INSN_CALL_DYNAMIC &&
396862306a36Sopenharmony_ci		    insn->type != INSN_RETURN)
396962306a36Sopenharmony_ci			continue;
397062306a36Sopenharmony_ci
397162306a36Sopenharmony_ci		if (insn->retpoline_safe)
397262306a36Sopenharmony_ci			continue;
397362306a36Sopenharmony_ci
397462306a36Sopenharmony_ci		if (insn->sec->init)
397562306a36Sopenharmony_ci			continue;
397662306a36Sopenharmony_ci
397762306a36Sopenharmony_ci		if (insn->type == INSN_RETURN) {
397862306a36Sopenharmony_ci			if (opts.rethunk) {
397962306a36Sopenharmony_ci				WARN_INSN(insn, "'naked' return found in RETHUNK build");
398062306a36Sopenharmony_ci			} else
398162306a36Sopenharmony_ci				continue;
398262306a36Sopenharmony_ci		} else {
398362306a36Sopenharmony_ci			WARN_INSN(insn, "indirect %s found in RETPOLINE build",
398462306a36Sopenharmony_ci				  insn->type == INSN_JUMP_DYNAMIC ? "jump" : "call");
398562306a36Sopenharmony_ci		}
398662306a36Sopenharmony_ci
398762306a36Sopenharmony_ci		warnings++;
398862306a36Sopenharmony_ci	}
398962306a36Sopenharmony_ci
399062306a36Sopenharmony_ci	return warnings;
399162306a36Sopenharmony_ci}
399262306a36Sopenharmony_ci
399362306a36Sopenharmony_cistatic bool is_kasan_insn(struct instruction *insn)
399462306a36Sopenharmony_ci{
399562306a36Sopenharmony_ci	return (insn->type == INSN_CALL &&
399662306a36Sopenharmony_ci		!strcmp(insn_call_dest(insn)->name, "__asan_handle_no_return"));
399762306a36Sopenharmony_ci}
399862306a36Sopenharmony_ci
399962306a36Sopenharmony_cistatic bool is_ubsan_insn(struct instruction *insn)
400062306a36Sopenharmony_ci{
400162306a36Sopenharmony_ci	return (insn->type == INSN_CALL &&
400262306a36Sopenharmony_ci		!strcmp(insn_call_dest(insn)->name,
400362306a36Sopenharmony_ci			"__ubsan_handle_builtin_unreachable"));
400462306a36Sopenharmony_ci}
400562306a36Sopenharmony_ci
400662306a36Sopenharmony_cistatic bool ignore_unreachable_insn(struct objtool_file *file, struct instruction *insn)
400762306a36Sopenharmony_ci{
400862306a36Sopenharmony_ci	int i;
400962306a36Sopenharmony_ci	struct instruction *prev_insn;
401062306a36Sopenharmony_ci
401162306a36Sopenharmony_ci	if (insn->ignore || insn->type == INSN_NOP || insn->type == INSN_TRAP)
401262306a36Sopenharmony_ci		return true;
401362306a36Sopenharmony_ci
401462306a36Sopenharmony_ci	/*
401562306a36Sopenharmony_ci	 * Ignore alternative replacement instructions.  This can happen
401662306a36Sopenharmony_ci	 * when a whitelisted function uses one of the ALTERNATIVE macros.
401762306a36Sopenharmony_ci	 */
401862306a36Sopenharmony_ci	if (!strcmp(insn->sec->name, ".altinstr_replacement") ||
401962306a36Sopenharmony_ci	    !strcmp(insn->sec->name, ".altinstr_aux"))
402062306a36Sopenharmony_ci		return true;
402162306a36Sopenharmony_ci
402262306a36Sopenharmony_ci	/*
402362306a36Sopenharmony_ci	 * Whole archive runs might encounter dead code from weak symbols.
402462306a36Sopenharmony_ci	 * This is where the linker will have dropped the weak symbol in
402562306a36Sopenharmony_ci	 * favour of a regular symbol, but leaves the code in place.
402662306a36Sopenharmony_ci	 *
402762306a36Sopenharmony_ci	 * In this case we'll find a piece of code (whole function) that is not
402862306a36Sopenharmony_ci	 * covered by a !section symbol. Ignore them.
402962306a36Sopenharmony_ci	 */
403062306a36Sopenharmony_ci	if (opts.link && !insn_func(insn)) {
403162306a36Sopenharmony_ci		int size = find_symbol_hole_containing(insn->sec, insn->offset);
403262306a36Sopenharmony_ci		unsigned long end = insn->offset + size;
403362306a36Sopenharmony_ci
403462306a36Sopenharmony_ci		if (!size) /* not a hole */
403562306a36Sopenharmony_ci			return false;
403662306a36Sopenharmony_ci
403762306a36Sopenharmony_ci		if (size < 0) /* hole until the end */
403862306a36Sopenharmony_ci			return true;
403962306a36Sopenharmony_ci
404062306a36Sopenharmony_ci		sec_for_each_insn_continue(file, insn) {
404162306a36Sopenharmony_ci			/*
404262306a36Sopenharmony_ci			 * If we reach a visited instruction at or before the
404362306a36Sopenharmony_ci			 * end of the hole, ignore the unreachable.
404462306a36Sopenharmony_ci			 */
404562306a36Sopenharmony_ci			if (insn->visited)
404662306a36Sopenharmony_ci				return true;
404762306a36Sopenharmony_ci
404862306a36Sopenharmony_ci			if (insn->offset >= end)
404962306a36Sopenharmony_ci				break;
405062306a36Sopenharmony_ci
405162306a36Sopenharmony_ci			/*
405262306a36Sopenharmony_ci			 * If this hole jumps to a .cold function, mark it ignore too.
405362306a36Sopenharmony_ci			 */
405462306a36Sopenharmony_ci			if (insn->jump_dest && insn_func(insn->jump_dest) &&
405562306a36Sopenharmony_ci			    strstr(insn_func(insn->jump_dest)->name, ".cold")) {
405662306a36Sopenharmony_ci				struct instruction *dest = insn->jump_dest;
405762306a36Sopenharmony_ci				func_for_each_insn(file, insn_func(dest), dest)
405862306a36Sopenharmony_ci					dest->ignore = true;
405962306a36Sopenharmony_ci			}
406062306a36Sopenharmony_ci		}
406162306a36Sopenharmony_ci
406262306a36Sopenharmony_ci		return false;
406362306a36Sopenharmony_ci	}
406462306a36Sopenharmony_ci
406562306a36Sopenharmony_ci	if (!insn_func(insn))
406662306a36Sopenharmony_ci		return false;
406762306a36Sopenharmony_ci
406862306a36Sopenharmony_ci	if (insn_func(insn)->static_call_tramp)
406962306a36Sopenharmony_ci		return true;
407062306a36Sopenharmony_ci
407162306a36Sopenharmony_ci	/*
407262306a36Sopenharmony_ci	 * CONFIG_UBSAN_TRAP inserts a UD2 when it sees
407362306a36Sopenharmony_ci	 * __builtin_unreachable().  The BUG() macro has an unreachable() after
407462306a36Sopenharmony_ci	 * the UD2, which causes GCC's undefined trap logic to emit another UD2
407562306a36Sopenharmony_ci	 * (or occasionally a JMP to UD2).
407662306a36Sopenharmony_ci	 *
407762306a36Sopenharmony_ci	 * It may also insert a UD2 after calling a __noreturn function.
407862306a36Sopenharmony_ci	 */
407962306a36Sopenharmony_ci	prev_insn = prev_insn_same_sec(file, insn);
408062306a36Sopenharmony_ci	if (prev_insn->dead_end &&
408162306a36Sopenharmony_ci	    (insn->type == INSN_BUG ||
408262306a36Sopenharmony_ci	     (insn->type == INSN_JUMP_UNCONDITIONAL &&
408362306a36Sopenharmony_ci	      insn->jump_dest && insn->jump_dest->type == INSN_BUG)))
408462306a36Sopenharmony_ci		return true;
408562306a36Sopenharmony_ci
408662306a36Sopenharmony_ci	/*
408762306a36Sopenharmony_ci	 * Check if this (or a subsequent) instruction is related to
408862306a36Sopenharmony_ci	 * CONFIG_UBSAN or CONFIG_KASAN.
408962306a36Sopenharmony_ci	 *
409062306a36Sopenharmony_ci	 * End the search at 5 instructions to avoid going into the weeds.
409162306a36Sopenharmony_ci	 */
409262306a36Sopenharmony_ci	for (i = 0; i < 5; i++) {
409362306a36Sopenharmony_ci
409462306a36Sopenharmony_ci		if (is_kasan_insn(insn) || is_ubsan_insn(insn))
409562306a36Sopenharmony_ci			return true;
409662306a36Sopenharmony_ci
409762306a36Sopenharmony_ci		if (insn->type == INSN_JUMP_UNCONDITIONAL) {
409862306a36Sopenharmony_ci			if (insn->jump_dest &&
409962306a36Sopenharmony_ci			    insn_func(insn->jump_dest) == insn_func(insn)) {
410062306a36Sopenharmony_ci				insn = insn->jump_dest;
410162306a36Sopenharmony_ci				continue;
410262306a36Sopenharmony_ci			}
410362306a36Sopenharmony_ci
410462306a36Sopenharmony_ci			break;
410562306a36Sopenharmony_ci		}
410662306a36Sopenharmony_ci
410762306a36Sopenharmony_ci		if (insn->offset + insn->len >= insn_func(insn)->offset + insn_func(insn)->len)
410862306a36Sopenharmony_ci			break;
410962306a36Sopenharmony_ci
411062306a36Sopenharmony_ci		insn = next_insn_same_sec(file, insn);
411162306a36Sopenharmony_ci	}
411262306a36Sopenharmony_ci
411362306a36Sopenharmony_ci	return false;
411462306a36Sopenharmony_ci}
411562306a36Sopenharmony_ci
411662306a36Sopenharmony_cistatic int add_prefix_symbol(struct objtool_file *file, struct symbol *func)
411762306a36Sopenharmony_ci{
411862306a36Sopenharmony_ci	struct instruction *insn, *prev;
411962306a36Sopenharmony_ci	struct cfi_state *cfi;
412062306a36Sopenharmony_ci
412162306a36Sopenharmony_ci	insn = find_insn(file, func->sec, func->offset);
412262306a36Sopenharmony_ci	if (!insn)
412362306a36Sopenharmony_ci		return -1;
412462306a36Sopenharmony_ci
412562306a36Sopenharmony_ci	for (prev = prev_insn_same_sec(file, insn);
412662306a36Sopenharmony_ci	     prev;
412762306a36Sopenharmony_ci	     prev = prev_insn_same_sec(file, prev)) {
412862306a36Sopenharmony_ci		u64 offset;
412962306a36Sopenharmony_ci
413062306a36Sopenharmony_ci		if (prev->type != INSN_NOP)
413162306a36Sopenharmony_ci			return -1;
413262306a36Sopenharmony_ci
413362306a36Sopenharmony_ci		offset = func->offset - prev->offset;
413462306a36Sopenharmony_ci
413562306a36Sopenharmony_ci		if (offset > opts.prefix)
413662306a36Sopenharmony_ci			return -1;
413762306a36Sopenharmony_ci
413862306a36Sopenharmony_ci		if (offset < opts.prefix)
413962306a36Sopenharmony_ci			continue;
414062306a36Sopenharmony_ci
414162306a36Sopenharmony_ci		elf_create_prefix_symbol(file->elf, func, opts.prefix);
414262306a36Sopenharmony_ci		break;
414362306a36Sopenharmony_ci	}
414462306a36Sopenharmony_ci
414562306a36Sopenharmony_ci	if (!prev)
414662306a36Sopenharmony_ci		return -1;
414762306a36Sopenharmony_ci
414862306a36Sopenharmony_ci	if (!insn->cfi) {
414962306a36Sopenharmony_ci		/*
415062306a36Sopenharmony_ci		 * This can happen if stack validation isn't enabled or the
415162306a36Sopenharmony_ci		 * function is annotated with STACK_FRAME_NON_STANDARD.
415262306a36Sopenharmony_ci		 */
415362306a36Sopenharmony_ci		return 0;
415462306a36Sopenharmony_ci	}
415562306a36Sopenharmony_ci
415662306a36Sopenharmony_ci	/* Propagate insn->cfi to the prefix code */
415762306a36Sopenharmony_ci	cfi = cfi_hash_find_or_add(insn->cfi);
415862306a36Sopenharmony_ci	for (; prev != insn; prev = next_insn_same_sec(file, prev))
415962306a36Sopenharmony_ci		prev->cfi = cfi;
416062306a36Sopenharmony_ci
416162306a36Sopenharmony_ci	return 0;
416262306a36Sopenharmony_ci}
416362306a36Sopenharmony_ci
416462306a36Sopenharmony_cistatic int add_prefix_symbols(struct objtool_file *file)
416562306a36Sopenharmony_ci{
416662306a36Sopenharmony_ci	struct section *sec;
416762306a36Sopenharmony_ci	struct symbol *func;
416862306a36Sopenharmony_ci
416962306a36Sopenharmony_ci	for_each_sec(file, sec) {
417062306a36Sopenharmony_ci		if (!(sec->sh.sh_flags & SHF_EXECINSTR))
417162306a36Sopenharmony_ci			continue;
417262306a36Sopenharmony_ci
417362306a36Sopenharmony_ci		sec_for_each_sym(sec, func) {
417462306a36Sopenharmony_ci			if (func->type != STT_FUNC)
417562306a36Sopenharmony_ci				continue;
417662306a36Sopenharmony_ci
417762306a36Sopenharmony_ci			add_prefix_symbol(file, func);
417862306a36Sopenharmony_ci		}
417962306a36Sopenharmony_ci	}
418062306a36Sopenharmony_ci
418162306a36Sopenharmony_ci	return 0;
418262306a36Sopenharmony_ci}
418362306a36Sopenharmony_ci
418462306a36Sopenharmony_cistatic int validate_symbol(struct objtool_file *file, struct section *sec,
418562306a36Sopenharmony_ci			   struct symbol *sym, struct insn_state *state)
418662306a36Sopenharmony_ci{
418762306a36Sopenharmony_ci	struct instruction *insn;
418862306a36Sopenharmony_ci	int ret;
418962306a36Sopenharmony_ci
419062306a36Sopenharmony_ci	if (!sym->len) {
419162306a36Sopenharmony_ci		WARN("%s() is missing an ELF size annotation", sym->name);
419262306a36Sopenharmony_ci		return 1;
419362306a36Sopenharmony_ci	}
419462306a36Sopenharmony_ci
419562306a36Sopenharmony_ci	if (sym->pfunc != sym || sym->alias != sym)
419662306a36Sopenharmony_ci		return 0;
419762306a36Sopenharmony_ci
419862306a36Sopenharmony_ci	insn = find_insn(file, sec, sym->offset);
419962306a36Sopenharmony_ci	if (!insn || insn->ignore || insn->visited)
420062306a36Sopenharmony_ci		return 0;
420162306a36Sopenharmony_ci
420262306a36Sopenharmony_ci	state->uaccess = sym->uaccess_safe;
420362306a36Sopenharmony_ci
420462306a36Sopenharmony_ci	ret = validate_branch(file, insn_func(insn), insn, *state);
420562306a36Sopenharmony_ci	if (ret)
420662306a36Sopenharmony_ci		BT_INSN(insn, "<=== (sym)");
420762306a36Sopenharmony_ci	return ret;
420862306a36Sopenharmony_ci}
420962306a36Sopenharmony_ci
421062306a36Sopenharmony_cistatic int validate_section(struct objtool_file *file, struct section *sec)
421162306a36Sopenharmony_ci{
421262306a36Sopenharmony_ci	struct insn_state state;
421362306a36Sopenharmony_ci	struct symbol *func;
421462306a36Sopenharmony_ci	int warnings = 0;
421562306a36Sopenharmony_ci
421662306a36Sopenharmony_ci	sec_for_each_sym(sec, func) {
421762306a36Sopenharmony_ci		if (func->type != STT_FUNC)
421862306a36Sopenharmony_ci			continue;
421962306a36Sopenharmony_ci
422062306a36Sopenharmony_ci		init_insn_state(file, &state, sec);
422162306a36Sopenharmony_ci		set_func_state(&state.cfi);
422262306a36Sopenharmony_ci
422362306a36Sopenharmony_ci		warnings += validate_symbol(file, sec, func, &state);
422462306a36Sopenharmony_ci	}
422562306a36Sopenharmony_ci
422662306a36Sopenharmony_ci	return warnings;
422762306a36Sopenharmony_ci}
422862306a36Sopenharmony_ci
422962306a36Sopenharmony_cistatic int validate_noinstr_sections(struct objtool_file *file)
423062306a36Sopenharmony_ci{
423162306a36Sopenharmony_ci	struct section *sec;
423262306a36Sopenharmony_ci	int warnings = 0;
423362306a36Sopenharmony_ci
423462306a36Sopenharmony_ci	sec = find_section_by_name(file->elf, ".noinstr.text");
423562306a36Sopenharmony_ci	if (sec) {
423662306a36Sopenharmony_ci		warnings += validate_section(file, sec);
423762306a36Sopenharmony_ci		warnings += validate_unwind_hints(file, sec);
423862306a36Sopenharmony_ci	}
423962306a36Sopenharmony_ci
424062306a36Sopenharmony_ci	sec = find_section_by_name(file->elf, ".entry.text");
424162306a36Sopenharmony_ci	if (sec) {
424262306a36Sopenharmony_ci		warnings += validate_section(file, sec);
424362306a36Sopenharmony_ci		warnings += validate_unwind_hints(file, sec);
424462306a36Sopenharmony_ci	}
424562306a36Sopenharmony_ci
424662306a36Sopenharmony_ci	sec = find_section_by_name(file->elf, ".cpuidle.text");
424762306a36Sopenharmony_ci	if (sec) {
424862306a36Sopenharmony_ci		warnings += validate_section(file, sec);
424962306a36Sopenharmony_ci		warnings += validate_unwind_hints(file, sec);
425062306a36Sopenharmony_ci	}
425162306a36Sopenharmony_ci
425262306a36Sopenharmony_ci	return warnings;
425362306a36Sopenharmony_ci}
425462306a36Sopenharmony_ci
425562306a36Sopenharmony_cistatic int validate_functions(struct objtool_file *file)
425662306a36Sopenharmony_ci{
425762306a36Sopenharmony_ci	struct section *sec;
425862306a36Sopenharmony_ci	int warnings = 0;
425962306a36Sopenharmony_ci
426062306a36Sopenharmony_ci	for_each_sec(file, sec) {
426162306a36Sopenharmony_ci		if (!(sec->sh.sh_flags & SHF_EXECINSTR))
426262306a36Sopenharmony_ci			continue;
426362306a36Sopenharmony_ci
426462306a36Sopenharmony_ci		warnings += validate_section(file, sec);
426562306a36Sopenharmony_ci	}
426662306a36Sopenharmony_ci
426762306a36Sopenharmony_ci	return warnings;
426862306a36Sopenharmony_ci}
426962306a36Sopenharmony_ci
427062306a36Sopenharmony_cistatic void mark_endbr_used(struct instruction *insn)
427162306a36Sopenharmony_ci{
427262306a36Sopenharmony_ci	if (!list_empty(&insn->call_node))
427362306a36Sopenharmony_ci		list_del_init(&insn->call_node);
427462306a36Sopenharmony_ci}
427562306a36Sopenharmony_ci
427662306a36Sopenharmony_cistatic bool noendbr_range(struct objtool_file *file, struct instruction *insn)
427762306a36Sopenharmony_ci{
427862306a36Sopenharmony_ci	struct symbol *sym = find_symbol_containing(insn->sec, insn->offset-1);
427962306a36Sopenharmony_ci	struct instruction *first;
428062306a36Sopenharmony_ci
428162306a36Sopenharmony_ci	if (!sym)
428262306a36Sopenharmony_ci		return false;
428362306a36Sopenharmony_ci
428462306a36Sopenharmony_ci	first = find_insn(file, sym->sec, sym->offset);
428562306a36Sopenharmony_ci	if (!first)
428662306a36Sopenharmony_ci		return false;
428762306a36Sopenharmony_ci
428862306a36Sopenharmony_ci	if (first->type != INSN_ENDBR && !first->noendbr)
428962306a36Sopenharmony_ci		return false;
429062306a36Sopenharmony_ci
429162306a36Sopenharmony_ci	return insn->offset == sym->offset + sym->len;
429262306a36Sopenharmony_ci}
429362306a36Sopenharmony_ci
429462306a36Sopenharmony_cistatic int validate_ibt_insn(struct objtool_file *file, struct instruction *insn)
429562306a36Sopenharmony_ci{
429662306a36Sopenharmony_ci	struct instruction *dest;
429762306a36Sopenharmony_ci	struct reloc *reloc;
429862306a36Sopenharmony_ci	unsigned long off;
429962306a36Sopenharmony_ci	int warnings = 0;
430062306a36Sopenharmony_ci
430162306a36Sopenharmony_ci	/*
430262306a36Sopenharmony_ci	 * Looking for function pointer load relocations.  Ignore
430362306a36Sopenharmony_ci	 * direct/indirect branches:
430462306a36Sopenharmony_ci	 */
430562306a36Sopenharmony_ci	switch (insn->type) {
430662306a36Sopenharmony_ci	case INSN_CALL:
430762306a36Sopenharmony_ci	case INSN_CALL_DYNAMIC:
430862306a36Sopenharmony_ci	case INSN_JUMP_CONDITIONAL:
430962306a36Sopenharmony_ci	case INSN_JUMP_UNCONDITIONAL:
431062306a36Sopenharmony_ci	case INSN_JUMP_DYNAMIC:
431162306a36Sopenharmony_ci	case INSN_JUMP_DYNAMIC_CONDITIONAL:
431262306a36Sopenharmony_ci	case INSN_RETURN:
431362306a36Sopenharmony_ci	case INSN_NOP:
431462306a36Sopenharmony_ci		return 0;
431562306a36Sopenharmony_ci	default:
431662306a36Sopenharmony_ci		break;
431762306a36Sopenharmony_ci	}
431862306a36Sopenharmony_ci
431962306a36Sopenharmony_ci	for (reloc = insn_reloc(file, insn);
432062306a36Sopenharmony_ci	     reloc;
432162306a36Sopenharmony_ci	     reloc = find_reloc_by_dest_range(file->elf, insn->sec,
432262306a36Sopenharmony_ci					      reloc_offset(reloc) + 1,
432362306a36Sopenharmony_ci					      (insn->offset + insn->len) - (reloc_offset(reloc) + 1))) {
432462306a36Sopenharmony_ci
432562306a36Sopenharmony_ci		/*
432662306a36Sopenharmony_ci		 * static_call_update() references the trampoline, which
432762306a36Sopenharmony_ci		 * doesn't have (or need) ENDBR.  Skip warning in that case.
432862306a36Sopenharmony_ci		 */
432962306a36Sopenharmony_ci		if (reloc->sym->static_call_tramp)
433062306a36Sopenharmony_ci			continue;
433162306a36Sopenharmony_ci
433262306a36Sopenharmony_ci		off = reloc->sym->offset;
433362306a36Sopenharmony_ci		if (reloc_type(reloc) == R_X86_64_PC32 ||
433462306a36Sopenharmony_ci		    reloc_type(reloc) == R_X86_64_PLT32)
433562306a36Sopenharmony_ci			off += arch_dest_reloc_offset(reloc_addend(reloc));
433662306a36Sopenharmony_ci		else
433762306a36Sopenharmony_ci			off += reloc_addend(reloc);
433862306a36Sopenharmony_ci
433962306a36Sopenharmony_ci		dest = find_insn(file, reloc->sym->sec, off);
434062306a36Sopenharmony_ci		if (!dest)
434162306a36Sopenharmony_ci			continue;
434262306a36Sopenharmony_ci
434362306a36Sopenharmony_ci		if (dest->type == INSN_ENDBR) {
434462306a36Sopenharmony_ci			mark_endbr_used(dest);
434562306a36Sopenharmony_ci			continue;
434662306a36Sopenharmony_ci		}
434762306a36Sopenharmony_ci
434862306a36Sopenharmony_ci		if (insn_func(dest) && insn_func(insn) &&
434962306a36Sopenharmony_ci		    insn_func(dest)->pfunc == insn_func(insn)->pfunc) {
435062306a36Sopenharmony_ci			/*
435162306a36Sopenharmony_ci			 * Anything from->to self is either _THIS_IP_ or
435262306a36Sopenharmony_ci			 * IRET-to-self.
435362306a36Sopenharmony_ci			 *
435462306a36Sopenharmony_ci			 * There is no sane way to annotate _THIS_IP_ since the
435562306a36Sopenharmony_ci			 * compiler treats the relocation as a constant and is
435662306a36Sopenharmony_ci			 * happy to fold in offsets, skewing any annotation we
435762306a36Sopenharmony_ci			 * do, leading to vast amounts of false-positives.
435862306a36Sopenharmony_ci			 *
435962306a36Sopenharmony_ci			 * There's also compiler generated _THIS_IP_ through
436062306a36Sopenharmony_ci			 * KCOV and such which we have no hope of annotating.
436162306a36Sopenharmony_ci			 *
436262306a36Sopenharmony_ci			 * As such, blanket accept self-references without
436362306a36Sopenharmony_ci			 * issue.
436462306a36Sopenharmony_ci			 */
436562306a36Sopenharmony_ci			continue;
436662306a36Sopenharmony_ci		}
436762306a36Sopenharmony_ci
436862306a36Sopenharmony_ci		/*
436962306a36Sopenharmony_ci		 * Accept anything ANNOTATE_NOENDBR.
437062306a36Sopenharmony_ci		 */
437162306a36Sopenharmony_ci		if (dest->noendbr)
437262306a36Sopenharmony_ci			continue;
437362306a36Sopenharmony_ci
437462306a36Sopenharmony_ci		/*
437562306a36Sopenharmony_ci		 * Accept if this is the instruction after a symbol
437662306a36Sopenharmony_ci		 * that is (no)endbr -- typical code-range usage.
437762306a36Sopenharmony_ci		 */
437862306a36Sopenharmony_ci		if (noendbr_range(file, dest))
437962306a36Sopenharmony_ci			continue;
438062306a36Sopenharmony_ci
438162306a36Sopenharmony_ci		WARN_INSN(insn, "relocation to !ENDBR: %s", offstr(dest->sec, dest->offset));
438262306a36Sopenharmony_ci
438362306a36Sopenharmony_ci		warnings++;
438462306a36Sopenharmony_ci	}
438562306a36Sopenharmony_ci
438662306a36Sopenharmony_ci	return warnings;
438762306a36Sopenharmony_ci}
438862306a36Sopenharmony_ci
438962306a36Sopenharmony_cistatic int validate_ibt_data_reloc(struct objtool_file *file,
439062306a36Sopenharmony_ci				   struct reloc *reloc)
439162306a36Sopenharmony_ci{
439262306a36Sopenharmony_ci	struct instruction *dest;
439362306a36Sopenharmony_ci
439462306a36Sopenharmony_ci	dest = find_insn(file, reloc->sym->sec,
439562306a36Sopenharmony_ci			 reloc->sym->offset + reloc_addend(reloc));
439662306a36Sopenharmony_ci	if (!dest)
439762306a36Sopenharmony_ci		return 0;
439862306a36Sopenharmony_ci
439962306a36Sopenharmony_ci	if (dest->type == INSN_ENDBR) {
440062306a36Sopenharmony_ci		mark_endbr_used(dest);
440162306a36Sopenharmony_ci		return 0;
440262306a36Sopenharmony_ci	}
440362306a36Sopenharmony_ci
440462306a36Sopenharmony_ci	if (dest->noendbr)
440562306a36Sopenharmony_ci		return 0;
440662306a36Sopenharmony_ci
440762306a36Sopenharmony_ci	WARN_FUNC("data relocation to !ENDBR: %s",
440862306a36Sopenharmony_ci		  reloc->sec->base, reloc_offset(reloc),
440962306a36Sopenharmony_ci		  offstr(dest->sec, dest->offset));
441062306a36Sopenharmony_ci
441162306a36Sopenharmony_ci	return 1;
441262306a36Sopenharmony_ci}
441362306a36Sopenharmony_ci
441462306a36Sopenharmony_ci/*
441562306a36Sopenharmony_ci * Validate IBT rules and remove used ENDBR instructions from the seal list.
441662306a36Sopenharmony_ci * Unused ENDBR instructions will be annotated for sealing (i.e., replaced with
441762306a36Sopenharmony_ci * NOPs) later, in create_ibt_endbr_seal_sections().
441862306a36Sopenharmony_ci */
441962306a36Sopenharmony_cistatic int validate_ibt(struct objtool_file *file)
442062306a36Sopenharmony_ci{
442162306a36Sopenharmony_ci	struct section *sec;
442262306a36Sopenharmony_ci	struct reloc *reloc;
442362306a36Sopenharmony_ci	struct instruction *insn;
442462306a36Sopenharmony_ci	int warnings = 0;
442562306a36Sopenharmony_ci
442662306a36Sopenharmony_ci	for_each_insn(file, insn)
442762306a36Sopenharmony_ci		warnings += validate_ibt_insn(file, insn);
442862306a36Sopenharmony_ci
442962306a36Sopenharmony_ci	for_each_sec(file, sec) {
443062306a36Sopenharmony_ci
443162306a36Sopenharmony_ci		/* Already done by validate_ibt_insn() */
443262306a36Sopenharmony_ci		if (sec->sh.sh_flags & SHF_EXECINSTR)
443362306a36Sopenharmony_ci			continue;
443462306a36Sopenharmony_ci
443562306a36Sopenharmony_ci		if (!sec->rsec)
443662306a36Sopenharmony_ci			continue;
443762306a36Sopenharmony_ci
443862306a36Sopenharmony_ci		/*
443962306a36Sopenharmony_ci		 * These sections can reference text addresses, but not with
444062306a36Sopenharmony_ci		 * the intent to indirect branch to them.
444162306a36Sopenharmony_ci		 */
444262306a36Sopenharmony_ci		if ((!strncmp(sec->name, ".discard", 8) &&
444362306a36Sopenharmony_ci		     strcmp(sec->name, ".discard.ibt_endbr_noseal"))	||
444462306a36Sopenharmony_ci		    !strncmp(sec->name, ".debug", 6)			||
444562306a36Sopenharmony_ci		    !strcmp(sec->name, ".altinstructions")		||
444662306a36Sopenharmony_ci		    !strcmp(sec->name, ".ibt_endbr_seal")		||
444762306a36Sopenharmony_ci		    !strcmp(sec->name, ".orc_unwind_ip")		||
444862306a36Sopenharmony_ci		    !strcmp(sec->name, ".parainstructions")		||
444962306a36Sopenharmony_ci		    !strcmp(sec->name, ".retpoline_sites")		||
445062306a36Sopenharmony_ci		    !strcmp(sec->name, ".smp_locks")			||
445162306a36Sopenharmony_ci		    !strcmp(sec->name, ".static_call_sites")		||
445262306a36Sopenharmony_ci		    !strcmp(sec->name, "_error_injection_whitelist")	||
445362306a36Sopenharmony_ci		    !strcmp(sec->name, "_kprobe_blacklist")		||
445462306a36Sopenharmony_ci		    !strcmp(sec->name, "__bug_table")			||
445562306a36Sopenharmony_ci		    !strcmp(sec->name, "__ex_table")			||
445662306a36Sopenharmony_ci		    !strcmp(sec->name, "__jump_table")			||
445762306a36Sopenharmony_ci		    !strcmp(sec->name, "__mcount_loc")			||
445862306a36Sopenharmony_ci		    !strcmp(sec->name, ".kcfi_traps")			||
445962306a36Sopenharmony_ci		    strstr(sec->name, "__patchable_function_entries"))
446062306a36Sopenharmony_ci			continue;
446162306a36Sopenharmony_ci
446262306a36Sopenharmony_ci		for_each_reloc(sec->rsec, reloc)
446362306a36Sopenharmony_ci			warnings += validate_ibt_data_reloc(file, reloc);
446462306a36Sopenharmony_ci	}
446562306a36Sopenharmony_ci
446662306a36Sopenharmony_ci	return warnings;
446762306a36Sopenharmony_ci}
446862306a36Sopenharmony_ci
446962306a36Sopenharmony_cistatic int validate_sls(struct objtool_file *file)
447062306a36Sopenharmony_ci{
447162306a36Sopenharmony_ci	struct instruction *insn, *next_insn;
447262306a36Sopenharmony_ci	int warnings = 0;
447362306a36Sopenharmony_ci
447462306a36Sopenharmony_ci	for_each_insn(file, insn) {
447562306a36Sopenharmony_ci		next_insn = next_insn_same_sec(file, insn);
447662306a36Sopenharmony_ci
447762306a36Sopenharmony_ci		if (insn->retpoline_safe)
447862306a36Sopenharmony_ci			continue;
447962306a36Sopenharmony_ci
448062306a36Sopenharmony_ci		switch (insn->type) {
448162306a36Sopenharmony_ci		case INSN_RETURN:
448262306a36Sopenharmony_ci			if (!next_insn || next_insn->type != INSN_TRAP) {
448362306a36Sopenharmony_ci				WARN_INSN(insn, "missing int3 after ret");
448462306a36Sopenharmony_ci				warnings++;
448562306a36Sopenharmony_ci			}
448662306a36Sopenharmony_ci
448762306a36Sopenharmony_ci			break;
448862306a36Sopenharmony_ci		case INSN_JUMP_DYNAMIC:
448962306a36Sopenharmony_ci			if (!next_insn || next_insn->type != INSN_TRAP) {
449062306a36Sopenharmony_ci				WARN_INSN(insn, "missing int3 after indirect jump");
449162306a36Sopenharmony_ci				warnings++;
449262306a36Sopenharmony_ci			}
449362306a36Sopenharmony_ci			break;
449462306a36Sopenharmony_ci		default:
449562306a36Sopenharmony_ci			break;
449662306a36Sopenharmony_ci		}
449762306a36Sopenharmony_ci	}
449862306a36Sopenharmony_ci
449962306a36Sopenharmony_ci	return warnings;
450062306a36Sopenharmony_ci}
450162306a36Sopenharmony_ci
450262306a36Sopenharmony_cistatic bool ignore_noreturn_call(struct instruction *insn)
450362306a36Sopenharmony_ci{
450462306a36Sopenharmony_ci	struct symbol *call_dest = insn_call_dest(insn);
450562306a36Sopenharmony_ci
450662306a36Sopenharmony_ci	/*
450762306a36Sopenharmony_ci	 * FIXME: hack, we need a real noreturn solution
450862306a36Sopenharmony_ci	 *
450962306a36Sopenharmony_ci	 * Problem is, exc_double_fault() may or may not return, depending on
451062306a36Sopenharmony_ci	 * whether CONFIG_X86_ESPFIX64 is set.  But objtool has no visibility
451162306a36Sopenharmony_ci	 * to the kernel config.
451262306a36Sopenharmony_ci	 *
451362306a36Sopenharmony_ci	 * Other potential ways to fix it:
451462306a36Sopenharmony_ci	 *
451562306a36Sopenharmony_ci	 *   - have compiler communicate __noreturn functions somehow
451662306a36Sopenharmony_ci	 *   - remove CONFIG_X86_ESPFIX64
451762306a36Sopenharmony_ci	 *   - read the .config file
451862306a36Sopenharmony_ci	 *   - add a cmdline option
451962306a36Sopenharmony_ci	 *   - create a generic objtool annotation format (vs a bunch of custom
452062306a36Sopenharmony_ci	 *     formats) and annotate it
452162306a36Sopenharmony_ci	 */
452262306a36Sopenharmony_ci	if (!strcmp(call_dest->name, "exc_double_fault")) {
452362306a36Sopenharmony_ci		/* prevent further unreachable warnings for the caller */
452462306a36Sopenharmony_ci		insn->sym->warned = 1;
452562306a36Sopenharmony_ci		return true;
452662306a36Sopenharmony_ci	}
452762306a36Sopenharmony_ci
452862306a36Sopenharmony_ci	return false;
452962306a36Sopenharmony_ci}
453062306a36Sopenharmony_ci
453162306a36Sopenharmony_cistatic int validate_reachable_instructions(struct objtool_file *file)
453262306a36Sopenharmony_ci{
453362306a36Sopenharmony_ci	struct instruction *insn, *prev_insn;
453462306a36Sopenharmony_ci	struct symbol *call_dest;
453562306a36Sopenharmony_ci	int warnings = 0;
453662306a36Sopenharmony_ci
453762306a36Sopenharmony_ci	if (file->ignore_unreachables)
453862306a36Sopenharmony_ci		return 0;
453962306a36Sopenharmony_ci
454062306a36Sopenharmony_ci	for_each_insn(file, insn) {
454162306a36Sopenharmony_ci		if (insn->visited || ignore_unreachable_insn(file, insn))
454262306a36Sopenharmony_ci			continue;
454362306a36Sopenharmony_ci
454462306a36Sopenharmony_ci		prev_insn = prev_insn_same_sec(file, insn);
454562306a36Sopenharmony_ci		if (prev_insn && prev_insn->dead_end) {
454662306a36Sopenharmony_ci			call_dest = insn_call_dest(prev_insn);
454762306a36Sopenharmony_ci			if (call_dest && !ignore_noreturn_call(prev_insn)) {
454862306a36Sopenharmony_ci				WARN_INSN(insn, "%s() is missing a __noreturn annotation",
454962306a36Sopenharmony_ci					  call_dest->name);
455062306a36Sopenharmony_ci				warnings++;
455162306a36Sopenharmony_ci				continue;
455262306a36Sopenharmony_ci			}
455362306a36Sopenharmony_ci		}
455462306a36Sopenharmony_ci
455562306a36Sopenharmony_ci		WARN_INSN(insn, "unreachable instruction");
455662306a36Sopenharmony_ci		warnings++;
455762306a36Sopenharmony_ci	}
455862306a36Sopenharmony_ci
455962306a36Sopenharmony_ci	return warnings;
456062306a36Sopenharmony_ci}
456162306a36Sopenharmony_ci
456262306a36Sopenharmony_ci/* 'funcs' is a space-separated list of function names */
456362306a36Sopenharmony_cistatic int disas_funcs(const char *funcs)
456462306a36Sopenharmony_ci{
456562306a36Sopenharmony_ci	const char *objdump_str, *cross_compile;
456662306a36Sopenharmony_ci	int size, ret;
456762306a36Sopenharmony_ci	char *cmd;
456862306a36Sopenharmony_ci
456962306a36Sopenharmony_ci	cross_compile = getenv("CROSS_COMPILE");
457062306a36Sopenharmony_ci
457162306a36Sopenharmony_ci	objdump_str = "%sobjdump -wdr %s | gawk -M -v _funcs='%s' '"
457262306a36Sopenharmony_ci			"BEGIN { split(_funcs, funcs); }"
457362306a36Sopenharmony_ci			"/^$/ { func_match = 0; }"
457462306a36Sopenharmony_ci			"/<.*>:/ { "
457562306a36Sopenharmony_ci				"f = gensub(/.*<(.*)>:/, \"\\\\1\", 1);"
457662306a36Sopenharmony_ci				"for (i in funcs) {"
457762306a36Sopenharmony_ci					"if (funcs[i] == f) {"
457862306a36Sopenharmony_ci						"func_match = 1;"
457962306a36Sopenharmony_ci						"base = strtonum(\"0x\" $1);"
458062306a36Sopenharmony_ci						"break;"
458162306a36Sopenharmony_ci					"}"
458262306a36Sopenharmony_ci				"}"
458362306a36Sopenharmony_ci			"}"
458462306a36Sopenharmony_ci			"{"
458562306a36Sopenharmony_ci				"if (func_match) {"
458662306a36Sopenharmony_ci					"addr = strtonum(\"0x\" $1);"
458762306a36Sopenharmony_ci					"printf(\"%%04x \", addr - base);"
458862306a36Sopenharmony_ci					"print;"
458962306a36Sopenharmony_ci				"}"
459062306a36Sopenharmony_ci			"}' 1>&2";
459162306a36Sopenharmony_ci
459262306a36Sopenharmony_ci	/* fake snprintf() to calculate the size */
459362306a36Sopenharmony_ci	size = snprintf(NULL, 0, objdump_str, cross_compile, objname, funcs) + 1;
459462306a36Sopenharmony_ci	if (size <= 0) {
459562306a36Sopenharmony_ci		WARN("objdump string size calculation failed");
459662306a36Sopenharmony_ci		return -1;
459762306a36Sopenharmony_ci	}
459862306a36Sopenharmony_ci
459962306a36Sopenharmony_ci	cmd = malloc(size);
460062306a36Sopenharmony_ci
460162306a36Sopenharmony_ci	/* real snprintf() */
460262306a36Sopenharmony_ci	snprintf(cmd, size, objdump_str, cross_compile, objname, funcs);
460362306a36Sopenharmony_ci	ret = system(cmd);
460462306a36Sopenharmony_ci	if (ret) {
460562306a36Sopenharmony_ci		WARN("disassembly failed: %d", ret);
460662306a36Sopenharmony_ci		return -1;
460762306a36Sopenharmony_ci	}
460862306a36Sopenharmony_ci
460962306a36Sopenharmony_ci	return 0;
461062306a36Sopenharmony_ci}
461162306a36Sopenharmony_ci
461262306a36Sopenharmony_cistatic int disas_warned_funcs(struct objtool_file *file)
461362306a36Sopenharmony_ci{
461462306a36Sopenharmony_ci	struct symbol *sym;
461562306a36Sopenharmony_ci	char *funcs = NULL, *tmp;
461662306a36Sopenharmony_ci
461762306a36Sopenharmony_ci	for_each_sym(file, sym) {
461862306a36Sopenharmony_ci		if (sym->warned) {
461962306a36Sopenharmony_ci			if (!funcs) {
462062306a36Sopenharmony_ci				funcs = malloc(strlen(sym->name) + 1);
462162306a36Sopenharmony_ci				strcpy(funcs, sym->name);
462262306a36Sopenharmony_ci			} else {
462362306a36Sopenharmony_ci				tmp = malloc(strlen(funcs) + strlen(sym->name) + 2);
462462306a36Sopenharmony_ci				sprintf(tmp, "%s %s", funcs, sym->name);
462562306a36Sopenharmony_ci				free(funcs);
462662306a36Sopenharmony_ci				funcs = tmp;
462762306a36Sopenharmony_ci			}
462862306a36Sopenharmony_ci		}
462962306a36Sopenharmony_ci	}
463062306a36Sopenharmony_ci
463162306a36Sopenharmony_ci	if (funcs)
463262306a36Sopenharmony_ci		disas_funcs(funcs);
463362306a36Sopenharmony_ci
463462306a36Sopenharmony_ci	return 0;
463562306a36Sopenharmony_ci}
463662306a36Sopenharmony_ci
463762306a36Sopenharmony_cistruct insn_chunk {
463862306a36Sopenharmony_ci	void *addr;
463962306a36Sopenharmony_ci	struct insn_chunk *next;
464062306a36Sopenharmony_ci};
464162306a36Sopenharmony_ci
464262306a36Sopenharmony_ci/*
464362306a36Sopenharmony_ci * Reduce peak RSS usage by freeing insns memory before writing the ELF file,
464462306a36Sopenharmony_ci * which can trigger more allocations for .debug_* sections whose data hasn't
464562306a36Sopenharmony_ci * been read yet.
464662306a36Sopenharmony_ci */
464762306a36Sopenharmony_cistatic void free_insns(struct objtool_file *file)
464862306a36Sopenharmony_ci{
464962306a36Sopenharmony_ci	struct instruction *insn;
465062306a36Sopenharmony_ci	struct insn_chunk *chunks = NULL, *chunk;
465162306a36Sopenharmony_ci
465262306a36Sopenharmony_ci	for_each_insn(file, insn) {
465362306a36Sopenharmony_ci		if (!insn->idx) {
465462306a36Sopenharmony_ci			chunk = malloc(sizeof(*chunk));
465562306a36Sopenharmony_ci			chunk->addr = insn;
465662306a36Sopenharmony_ci			chunk->next = chunks;
465762306a36Sopenharmony_ci			chunks = chunk;
465862306a36Sopenharmony_ci		}
465962306a36Sopenharmony_ci	}
466062306a36Sopenharmony_ci
466162306a36Sopenharmony_ci	for (chunk = chunks; chunk; chunk = chunk->next)
466262306a36Sopenharmony_ci		free(chunk->addr);
466362306a36Sopenharmony_ci}
466462306a36Sopenharmony_ci
466562306a36Sopenharmony_ciint check(struct objtool_file *file)
466662306a36Sopenharmony_ci{
466762306a36Sopenharmony_ci	int ret, warnings = 0;
466862306a36Sopenharmony_ci
466962306a36Sopenharmony_ci	arch_initial_func_cfi_state(&initial_func_cfi);
467062306a36Sopenharmony_ci	init_cfi_state(&init_cfi);
467162306a36Sopenharmony_ci	init_cfi_state(&func_cfi);
467262306a36Sopenharmony_ci	set_func_state(&func_cfi);
467362306a36Sopenharmony_ci	init_cfi_state(&force_undefined_cfi);
467462306a36Sopenharmony_ci	force_undefined_cfi.force_undefined = true;
467562306a36Sopenharmony_ci
467662306a36Sopenharmony_ci	if (!cfi_hash_alloc(1UL << (file->elf->symbol_bits - 3)))
467762306a36Sopenharmony_ci		goto out;
467862306a36Sopenharmony_ci
467962306a36Sopenharmony_ci	cfi_hash_add(&init_cfi);
468062306a36Sopenharmony_ci	cfi_hash_add(&func_cfi);
468162306a36Sopenharmony_ci
468262306a36Sopenharmony_ci	ret = decode_sections(file);
468362306a36Sopenharmony_ci	if (ret < 0)
468462306a36Sopenharmony_ci		goto out;
468562306a36Sopenharmony_ci
468662306a36Sopenharmony_ci	warnings += ret;
468762306a36Sopenharmony_ci
468862306a36Sopenharmony_ci	if (!nr_insns)
468962306a36Sopenharmony_ci		goto out;
469062306a36Sopenharmony_ci
469162306a36Sopenharmony_ci	if (opts.retpoline) {
469262306a36Sopenharmony_ci		ret = validate_retpoline(file);
469362306a36Sopenharmony_ci		if (ret < 0)
469462306a36Sopenharmony_ci			return ret;
469562306a36Sopenharmony_ci		warnings += ret;
469662306a36Sopenharmony_ci	}
469762306a36Sopenharmony_ci
469862306a36Sopenharmony_ci	if (opts.stackval || opts.orc || opts.uaccess) {
469962306a36Sopenharmony_ci		ret = validate_functions(file);
470062306a36Sopenharmony_ci		if (ret < 0)
470162306a36Sopenharmony_ci			goto out;
470262306a36Sopenharmony_ci		warnings += ret;
470362306a36Sopenharmony_ci
470462306a36Sopenharmony_ci		ret = validate_unwind_hints(file, NULL);
470562306a36Sopenharmony_ci		if (ret < 0)
470662306a36Sopenharmony_ci			goto out;
470762306a36Sopenharmony_ci		warnings += ret;
470862306a36Sopenharmony_ci
470962306a36Sopenharmony_ci		if (!warnings) {
471062306a36Sopenharmony_ci			ret = validate_reachable_instructions(file);
471162306a36Sopenharmony_ci			if (ret < 0)
471262306a36Sopenharmony_ci				goto out;
471362306a36Sopenharmony_ci			warnings += ret;
471462306a36Sopenharmony_ci		}
471562306a36Sopenharmony_ci
471662306a36Sopenharmony_ci	} else if (opts.noinstr) {
471762306a36Sopenharmony_ci		ret = validate_noinstr_sections(file);
471862306a36Sopenharmony_ci		if (ret < 0)
471962306a36Sopenharmony_ci			goto out;
472062306a36Sopenharmony_ci		warnings += ret;
472162306a36Sopenharmony_ci	}
472262306a36Sopenharmony_ci
472362306a36Sopenharmony_ci	if (opts.unret) {
472462306a36Sopenharmony_ci		/*
472562306a36Sopenharmony_ci		 * Must be after validate_branch() and friends, it plays
472662306a36Sopenharmony_ci		 * further games with insn->visited.
472762306a36Sopenharmony_ci		 */
472862306a36Sopenharmony_ci		ret = validate_unrets(file);
472962306a36Sopenharmony_ci		if (ret < 0)
473062306a36Sopenharmony_ci			return ret;
473162306a36Sopenharmony_ci		warnings += ret;
473262306a36Sopenharmony_ci	}
473362306a36Sopenharmony_ci
473462306a36Sopenharmony_ci	if (opts.ibt) {
473562306a36Sopenharmony_ci		ret = validate_ibt(file);
473662306a36Sopenharmony_ci		if (ret < 0)
473762306a36Sopenharmony_ci			goto out;
473862306a36Sopenharmony_ci		warnings += ret;
473962306a36Sopenharmony_ci	}
474062306a36Sopenharmony_ci
474162306a36Sopenharmony_ci	if (opts.sls) {
474262306a36Sopenharmony_ci		ret = validate_sls(file);
474362306a36Sopenharmony_ci		if (ret < 0)
474462306a36Sopenharmony_ci			goto out;
474562306a36Sopenharmony_ci		warnings += ret;
474662306a36Sopenharmony_ci	}
474762306a36Sopenharmony_ci
474862306a36Sopenharmony_ci	if (opts.static_call) {
474962306a36Sopenharmony_ci		ret = create_static_call_sections(file);
475062306a36Sopenharmony_ci		if (ret < 0)
475162306a36Sopenharmony_ci			goto out;
475262306a36Sopenharmony_ci		warnings += ret;
475362306a36Sopenharmony_ci	}
475462306a36Sopenharmony_ci
475562306a36Sopenharmony_ci	if (opts.retpoline) {
475662306a36Sopenharmony_ci		ret = create_retpoline_sites_sections(file);
475762306a36Sopenharmony_ci		if (ret < 0)
475862306a36Sopenharmony_ci			goto out;
475962306a36Sopenharmony_ci		warnings += ret;
476062306a36Sopenharmony_ci	}
476162306a36Sopenharmony_ci
476262306a36Sopenharmony_ci	if (opts.cfi) {
476362306a36Sopenharmony_ci		ret = create_cfi_sections(file);
476462306a36Sopenharmony_ci		if (ret < 0)
476562306a36Sopenharmony_ci			goto out;
476662306a36Sopenharmony_ci		warnings += ret;
476762306a36Sopenharmony_ci	}
476862306a36Sopenharmony_ci
476962306a36Sopenharmony_ci	if (opts.rethunk) {
477062306a36Sopenharmony_ci		ret = create_return_sites_sections(file);
477162306a36Sopenharmony_ci		if (ret < 0)
477262306a36Sopenharmony_ci			goto out;
477362306a36Sopenharmony_ci		warnings += ret;
477462306a36Sopenharmony_ci
477562306a36Sopenharmony_ci		if (opts.hack_skylake) {
477662306a36Sopenharmony_ci			ret = create_direct_call_sections(file);
477762306a36Sopenharmony_ci			if (ret < 0)
477862306a36Sopenharmony_ci				goto out;
477962306a36Sopenharmony_ci			warnings += ret;
478062306a36Sopenharmony_ci		}
478162306a36Sopenharmony_ci	}
478262306a36Sopenharmony_ci
478362306a36Sopenharmony_ci	if (opts.mcount) {
478462306a36Sopenharmony_ci		ret = create_mcount_loc_sections(file);
478562306a36Sopenharmony_ci		if (ret < 0)
478662306a36Sopenharmony_ci			goto out;
478762306a36Sopenharmony_ci		warnings += ret;
478862306a36Sopenharmony_ci	}
478962306a36Sopenharmony_ci
479062306a36Sopenharmony_ci	if (opts.prefix) {
479162306a36Sopenharmony_ci		ret = add_prefix_symbols(file);
479262306a36Sopenharmony_ci		if (ret < 0)
479362306a36Sopenharmony_ci			return ret;
479462306a36Sopenharmony_ci		warnings += ret;
479562306a36Sopenharmony_ci	}
479662306a36Sopenharmony_ci
479762306a36Sopenharmony_ci	if (opts.ibt) {
479862306a36Sopenharmony_ci		ret = create_ibt_endbr_seal_sections(file);
479962306a36Sopenharmony_ci		if (ret < 0)
480062306a36Sopenharmony_ci			goto out;
480162306a36Sopenharmony_ci		warnings += ret;
480262306a36Sopenharmony_ci	}
480362306a36Sopenharmony_ci
480462306a36Sopenharmony_ci	if (opts.orc && nr_insns) {
480562306a36Sopenharmony_ci		ret = orc_create(file);
480662306a36Sopenharmony_ci		if (ret < 0)
480762306a36Sopenharmony_ci			goto out;
480862306a36Sopenharmony_ci		warnings += ret;
480962306a36Sopenharmony_ci	}
481062306a36Sopenharmony_ci
481162306a36Sopenharmony_ci	free_insns(file);
481262306a36Sopenharmony_ci
481362306a36Sopenharmony_ci	if (opts.verbose)
481462306a36Sopenharmony_ci		disas_warned_funcs(file);
481562306a36Sopenharmony_ci
481662306a36Sopenharmony_ci	if (opts.stats) {
481762306a36Sopenharmony_ci		printf("nr_insns_visited: %ld\n", nr_insns_visited);
481862306a36Sopenharmony_ci		printf("nr_cfi: %ld\n", nr_cfi);
481962306a36Sopenharmony_ci		printf("nr_cfi_reused: %ld\n", nr_cfi_reused);
482062306a36Sopenharmony_ci		printf("nr_cfi_cache: %ld\n", nr_cfi_cache);
482162306a36Sopenharmony_ci	}
482262306a36Sopenharmony_ci
482362306a36Sopenharmony_ciout:
482462306a36Sopenharmony_ci	/*
482562306a36Sopenharmony_ci	 *  For now, don't fail the kernel build on fatal warnings.  These
482662306a36Sopenharmony_ci	 *  errors are still fairly common due to the growing matrix of
482762306a36Sopenharmony_ci	 *  supported toolchains and their recent pace of change.
482862306a36Sopenharmony_ci	 */
482962306a36Sopenharmony_ci	return 0;
483062306a36Sopenharmony_ci}
4831