xref: /linux-6.15/tools/objtool/check.c (revision 19f5ca46)
11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2dcc914f4SJosh Poimboeuf /*
3dcc914f4SJosh Poimboeuf  * Copyright (C) 2015-2017 Josh Poimboeuf <[email protected]>
4dcc914f4SJosh Poimboeuf  */
5dcc914f4SJosh Poimboeuf 
6dcc914f4SJosh Poimboeuf #include <string.h>
7dcc914f4SJosh Poimboeuf #include <stdlib.h>
822682a07SMikulas Patocka #include <inttypes.h>
98b946cc3SPeter Zijlstra #include <sys/mman.h>
10dcc914f4SJosh Poimboeuf 
117786032eSVasily Gorbik #include <objtool/builtin.h>
127786032eSVasily Gorbik #include <objtool/cfi.h>
137786032eSVasily Gorbik #include <objtool/arch.h>
147786032eSVasily Gorbik #include <objtool/check.h>
157786032eSVasily Gorbik #include <objtool/special.h>
167786032eSVasily Gorbik #include <objtool/warn.h>
177786032eSVasily Gorbik #include <objtool/endianness.h>
18dcc914f4SJosh Poimboeuf 
19f7515d9fSJosh Poimboeuf #include <linux/objtool_types.h>
20dcc914f4SJosh Poimboeuf #include <linux/hashtable.h>
21dcc914f4SJosh Poimboeuf #include <linux/kernel.h>
221e7e4788SJosh Poimboeuf #include <linux/static_call_types.h>
23d5ab2bc3STiezhu Yang #include <linux/string.h>
24dcc914f4SJosh Poimboeuf 
25dcc914f4SJosh Poimboeuf struct alternative {
26d5406654SPeter Zijlstra 	struct alternative *next;
27dcc914f4SJosh Poimboeuf 	struct instruction *insn;
28dcc914f4SJosh Poimboeuf };
29dcc914f4SJosh Poimboeuf 
308b946cc3SPeter Zijlstra static unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache;
318b946cc3SPeter Zijlstra 
328b946cc3SPeter Zijlstra static struct cfi_init_state initial_func_cfi;
338b946cc3SPeter Zijlstra static struct cfi_state init_cfi;
348b946cc3SPeter Zijlstra static struct cfi_state func_cfi;
351e4b6191SJosh Poimboeuf static struct cfi_state force_undefined_cfi;
36dcc914f4SJosh Poimboeuf 
find_insn(struct objtool_file * file,struct section * sec,unsigned long offset)37627fce14SJosh Poimboeuf struct instruction *find_insn(struct objtool_file *file,
38dcc914f4SJosh Poimboeuf 			      struct section *sec, unsigned long offset)
39dcc914f4SJosh Poimboeuf {
40dcc914f4SJosh Poimboeuf 	struct instruction *insn;
41dcc914f4SJosh Poimboeuf 
4287ecb582SPeter Zijlstra 	hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, offset)) {
43dcc914f4SJosh Poimboeuf 		if (insn->sec == sec && insn->offset == offset)
44dcc914f4SJosh Poimboeuf 			return insn;
4587ecb582SPeter Zijlstra 	}
46dcc914f4SJosh Poimboeuf 
47dcc914f4SJosh Poimboeuf 	return NULL;
48dcc914f4SJosh Poimboeuf }
49dcc914f4SJosh Poimboeuf 
next_insn_same_sec(struct objtool_file * file,struct instruction * insn)501c34496eSPeter Zijlstra struct instruction *next_insn_same_sec(struct objtool_file *file,
51dcc914f4SJosh Poimboeuf 				       struct instruction *insn)
52dcc914f4SJosh Poimboeuf {
531c34496eSPeter Zijlstra 	if (insn->idx == INSN_CHUNK_MAX)
541c34496eSPeter Zijlstra 		return find_insn(file, insn->sec, insn->offset + insn->len);
55dcc914f4SJosh Poimboeuf 
561c34496eSPeter Zijlstra 	insn++;
571c34496eSPeter Zijlstra 	if (!insn->len)
58dcc914f4SJosh Poimboeuf 		return NULL;
59dcc914f4SJosh Poimboeuf 
601c34496eSPeter Zijlstra 	return insn;
61dcc914f4SJosh Poimboeuf }
62dcc914f4SJosh Poimboeuf 
next_insn_same_func(struct objtool_file * file,struct instruction * insn)6313810435SJosh Poimboeuf static struct instruction *next_insn_same_func(struct objtool_file *file,
6413810435SJosh Poimboeuf 					       struct instruction *insn)
6513810435SJosh Poimboeuf {
661c34496eSPeter Zijlstra 	struct instruction *next = next_insn_same_sec(file, insn);
67dbcdbdfdSPeter Zijlstra 	struct symbol *func = insn_func(insn);
6813810435SJosh Poimboeuf 
6913810435SJosh Poimboeuf 	if (!func)
7013810435SJosh Poimboeuf 		return NULL;
7113810435SJosh Poimboeuf 
721c34496eSPeter Zijlstra 	if (next && insn_func(next) == func)
7313810435SJosh Poimboeuf 		return next;
7413810435SJosh Poimboeuf 
7513810435SJosh Poimboeuf 	/* Check if we're already in the subfunction: */
7613810435SJosh Poimboeuf 	if (func == func->cfunc)
7713810435SJosh Poimboeuf 		return NULL;
7813810435SJosh Poimboeuf 
7913810435SJosh Poimboeuf 	/* Move to the subfunction: */
8013810435SJosh Poimboeuf 	return find_insn(file, func->cfunc->sec, func->cfunc->offset);
8113810435SJosh Poimboeuf }
8213810435SJosh Poimboeuf 
prev_insn_same_sec(struct objtool_file * file,struct instruction * insn)831c34496eSPeter Zijlstra static struct instruction *prev_insn_same_sec(struct objtool_file *file,
841c34496eSPeter Zijlstra 					      struct instruction *insn)
851c34496eSPeter Zijlstra {
861c34496eSPeter Zijlstra 	if (insn->idx == 0) {
871c34496eSPeter Zijlstra 		if (insn->prev_len)
881c34496eSPeter Zijlstra 			return find_insn(file, insn->sec, insn->offset - insn->prev_len);
891c34496eSPeter Zijlstra 		return NULL;
901c34496eSPeter Zijlstra 	}
911c34496eSPeter Zijlstra 
921c34496eSPeter Zijlstra 	return insn - 1;
931c34496eSPeter Zijlstra }
941c34496eSPeter Zijlstra 
prev_insn_same_sym(struct objtool_file * file,struct instruction * insn)951119d265SJosh Poimboeuf static struct instruction *prev_insn_same_sym(struct objtool_file *file,
961119d265SJosh Poimboeuf 					      struct instruction *insn)
971119d265SJosh Poimboeuf {
981c34496eSPeter Zijlstra 	struct instruction *prev = prev_insn_same_sec(file, insn);
991119d265SJosh Poimboeuf 
1001c34496eSPeter Zijlstra 	if (prev && insn_func(prev) == insn_func(insn))
1011119d265SJosh Poimboeuf 		return prev;
1021119d265SJosh Poimboeuf 
1031119d265SJosh Poimboeuf 	return NULL;
1041119d265SJosh Poimboeuf }
1051119d265SJosh Poimboeuf 
1061c34496eSPeter Zijlstra #define for_each_insn(file, insn)					\
1071c34496eSPeter Zijlstra 	for (struct section *__sec, *__fake = (struct section *)1;	\
1081c34496eSPeter Zijlstra 	     __fake; __fake = NULL)					\
1091c34496eSPeter Zijlstra 		for_each_sec(file, __sec)				\
1101c34496eSPeter Zijlstra 			sec_for_each_insn(file, __sec, insn)
1111c34496eSPeter Zijlstra 
112f0f70adbSPeter Zijlstra #define func_for_each_insn(file, func, insn)				\
11313810435SJosh Poimboeuf 	for (insn = find_insn(file, func->sec, func->offset);		\
11413810435SJosh Poimboeuf 	     insn;							\
11513810435SJosh Poimboeuf 	     insn = next_insn_same_func(file, insn))
11613810435SJosh Poimboeuf 
117dbf4aeb0SPeter Zijlstra #define sym_for_each_insn(file, sym, insn)				\
118dbf4aeb0SPeter Zijlstra 	for (insn = find_insn(file, sym->sec, sym->offset);		\
1191c34496eSPeter Zijlstra 	     insn && insn->offset < sym->offset + sym->len;		\
1201c34496eSPeter Zijlstra 	     insn = next_insn_same_sec(file, insn))
121dcc914f4SJosh Poimboeuf 
122dbf4aeb0SPeter Zijlstra #define sym_for_each_insn_continue_reverse(file, sym, insn)		\
1231c34496eSPeter Zijlstra 	for (insn = prev_insn_same_sec(file, insn);			\
1241c34496eSPeter Zijlstra 	     insn && insn->offset >= sym->offset;			\
1251c34496eSPeter Zijlstra 	     insn = prev_insn_same_sec(file, insn))
126dcc914f4SJosh Poimboeuf 
127dcc914f4SJosh Poimboeuf #define sec_for_each_insn_from(file, insn)				\
128dcc914f4SJosh Poimboeuf 	for (; insn; insn = next_insn_same_sec(file, insn))
129dcc914f4SJosh Poimboeuf 
130baa41469SJosh Poimboeuf #define sec_for_each_insn_continue(file, insn)				\
131baa41469SJosh Poimboeuf 	for (insn = next_insn_same_sec(file, insn); insn;		\
132baa41469SJosh Poimboeuf 	     insn = next_insn_same_sec(file, insn))
133dcc914f4SJosh Poimboeuf 
insn_call_dest(struct instruction * insn)134c6f5dc28SPeter Zijlstra static inline struct symbol *insn_call_dest(struct instruction *insn)
135c6f5dc28SPeter Zijlstra {
136c6f5dc28SPeter Zijlstra 	if (insn->type == INSN_JUMP_DYNAMIC ||
137c6f5dc28SPeter Zijlstra 	    insn->type == INSN_CALL_DYNAMIC)
138c6f5dc28SPeter Zijlstra 		return NULL;
139c6f5dc28SPeter Zijlstra 
140c6f5dc28SPeter Zijlstra 	return insn->_call_dest;
141c6f5dc28SPeter Zijlstra }
142c6f5dc28SPeter Zijlstra 
insn_jump_table(struct instruction * insn)143c6f5dc28SPeter Zijlstra static inline struct reloc *insn_jump_table(struct instruction *insn)
144c6f5dc28SPeter Zijlstra {
145c6f5dc28SPeter Zijlstra 	if (insn->type == INSN_JUMP_DYNAMIC ||
146c6f5dc28SPeter Zijlstra 	    insn->type == INSN_CALL_DYNAMIC)
147c6f5dc28SPeter Zijlstra 		return insn->_jump_table;
148c6f5dc28SPeter Zijlstra 
149c6f5dc28SPeter Zijlstra 	return NULL;
150c6f5dc28SPeter Zijlstra }
151c6f5dc28SPeter Zijlstra 
insn_jump_table_size(struct instruction * insn)152c3cb6c15SArd Biesheuvel static inline unsigned long insn_jump_table_size(struct instruction *insn)
153c3cb6c15SArd Biesheuvel {
154c3cb6c15SArd Biesheuvel 	if (insn->type == INSN_JUMP_DYNAMIC ||
155c3cb6c15SArd Biesheuvel 	    insn->type == INSN_CALL_DYNAMIC)
156c3cb6c15SArd Biesheuvel 		return insn->_jump_table_size;
157c3cb6c15SArd Biesheuvel 
158c3cb6c15SArd Biesheuvel 	return 0;
159c3cb6c15SArd Biesheuvel }
160c3cb6c15SArd Biesheuvel 
is_jump_table_jump(struct instruction * insn)16199033461SJosh Poimboeuf static bool is_jump_table_jump(struct instruction *insn)
16299033461SJosh Poimboeuf {
16399033461SJosh Poimboeuf 	struct alt_group *alt_group = insn->alt_group;
16499033461SJosh Poimboeuf 
165c6f5dc28SPeter Zijlstra 	if (insn_jump_table(insn))
16699033461SJosh Poimboeuf 		return true;
16799033461SJosh Poimboeuf 
16899033461SJosh Poimboeuf 	/* Retpoline alternative for a jump table? */
16999033461SJosh Poimboeuf 	return alt_group && alt_group->orig_group &&
170c6f5dc28SPeter Zijlstra 	       insn_jump_table(alt_group->orig_group->first_insn);
17199033461SJosh Poimboeuf }
17299033461SJosh Poimboeuf 
is_sibling_call(struct instruction * insn)1730c1ddd33SJosh Poimboeuf static bool is_sibling_call(struct instruction *insn)
1740c1ddd33SJosh Poimboeuf {
175ecf11ba4SJosh Poimboeuf 	/*
1765a9c361aSPeter Zijlstra 	 * Assume only STT_FUNC calls have jump-tables.
177ecf11ba4SJosh Poimboeuf 	 */
1785a9c361aSPeter Zijlstra 	if (insn_func(insn)) {
1790c1ddd33SJosh Poimboeuf 		/* An indirect jump is either a sibling call or a jump to a table. */
1800c1ddd33SJosh Poimboeuf 		if (insn->type == INSN_JUMP_DYNAMIC)
18199033461SJosh Poimboeuf 			return !is_jump_table_jump(insn);
1825a9c361aSPeter Zijlstra 	}
1830c1ddd33SJosh Poimboeuf 
184c6f5dc28SPeter Zijlstra 	/* add_jump_destinations() sets insn_call_dest(insn) for sibling calls. */
185c6f5dc28SPeter Zijlstra 	return (is_static_jump(insn) && insn_call_dest(insn));
1860c1ddd33SJosh Poimboeuf }
1870c1ddd33SJosh Poimboeuf 
188dcc914f4SJosh Poimboeuf /*
18956d680ddSMiguel Ojeda  * Checks if a string ends with another.
19056d680ddSMiguel Ojeda  */
str_ends_with(const char * s,const char * sub)19156d680ddSMiguel Ojeda static bool str_ends_with(const char *s, const char *sub)
19256d680ddSMiguel Ojeda {
19356d680ddSMiguel Ojeda 	const int slen = strlen(s);
19456d680ddSMiguel Ojeda 	const int sublen = strlen(sub);
19556d680ddSMiguel Ojeda 
19656d680ddSMiguel Ojeda 	if (sublen > slen)
19756d680ddSMiguel Ojeda 		return 0;
19856d680ddSMiguel Ojeda 
19956d680ddSMiguel Ojeda 	return !memcmp(s + slen - sublen, sub, sublen);
20056d680ddSMiguel Ojeda }
20156d680ddSMiguel Ojeda 
20256d680ddSMiguel Ojeda /*
20356d680ddSMiguel Ojeda  * Checks if a function is a Rust "noreturn" one.
20456d680ddSMiguel Ojeda  */
is_rust_noreturn(const struct symbol * func)20556d680ddSMiguel Ojeda static bool is_rust_noreturn(const struct symbol *func)
20656d680ddSMiguel Ojeda {
20756d680ddSMiguel Ojeda 	/*
20856d680ddSMiguel Ojeda 	 * If it does not start with "_R", then it is not a Rust symbol.
20956d680ddSMiguel Ojeda 	 */
21056d680ddSMiguel Ojeda 	if (strncmp(func->name, "_R", 2))
21156d680ddSMiguel Ojeda 		return false;
21256d680ddSMiguel Ojeda 
21356d680ddSMiguel Ojeda 	/*
21456d680ddSMiguel Ojeda 	 * These are just heuristics -- we do not control the precise symbol
21556d680ddSMiguel Ojeda 	 * name, due to the crate disambiguators (which depend on the compiler)
21656d680ddSMiguel Ojeda 	 * as well as changes to the source code itself between versions (since
21756d680ddSMiguel Ojeda 	 * these come from the Rust standard library).
21856d680ddSMiguel Ojeda 	 */
21956d680ddSMiguel Ojeda 	return str_ends_with(func->name, "_4core5sliceSp15copy_from_slice17len_mismatch_fail")		||
22056d680ddSMiguel Ojeda 	       str_ends_with(func->name, "_4core6option13unwrap_failed")				||
22156d680ddSMiguel Ojeda 	       str_ends_with(func->name, "_4core6result13unwrap_failed")				||
22256d680ddSMiguel Ojeda 	       str_ends_with(func->name, "_4core9panicking5panic")					||
22356d680ddSMiguel Ojeda 	       str_ends_with(func->name, "_4core9panicking9panic_fmt")					||
22456d680ddSMiguel Ojeda 	       str_ends_with(func->name, "_4core9panicking14panic_explicit")				||
22556d680ddSMiguel Ojeda 	       str_ends_with(func->name, "_4core9panicking14panic_nounwind")				||
22656d680ddSMiguel Ojeda 	       str_ends_with(func->name, "_4core9panicking18panic_bounds_check")			||
22756d680ddSMiguel Ojeda 	       str_ends_with(func->name, "_4core9panicking19assert_failed_inner")			||
228a3cd5f50SMiguel Ojeda 	       str_ends_with(func->name, "_4core9panicking30panic_null_pointer_dereference")		||
22956d680ddSMiguel Ojeda 	       str_ends_with(func->name, "_4core9panicking36panic_misaligned_pointer_dereference")	||
230*19f5ca46SMiguel Ojeda 	       str_ends_with(func->name, "_7___rustc17rust_begin_unwind")				||
231cee6f9a9SMiguel Ojeda 	       strstr(func->name, "_4core9panicking13assert_failed")					||
23256d680ddSMiguel Ojeda 	       strstr(func->name, "_4core9panicking11panic_const24panic_const_")			||
23356d680ddSMiguel Ojeda 	       (strstr(func->name, "_4core5slice5index24slice_") &&
23456d680ddSMiguel Ojeda 		str_ends_with(func->name, "_fail"));
23556d680ddSMiguel Ojeda }
23656d680ddSMiguel Ojeda 
23756d680ddSMiguel Ojeda /*
238dcc914f4SJosh Poimboeuf  * This checks to see if the given function is a "noreturn" function.
239dcc914f4SJosh Poimboeuf  *
240dcc914f4SJosh Poimboeuf  * For global functions which are outside the scope of this object file, we
241dcc914f4SJosh Poimboeuf  * have to keep a manual list of them.
242dcc914f4SJosh Poimboeuf  *
243dcc914f4SJosh Poimboeuf  * For local functions, we have to detect them manually by simply looking for
244dcc914f4SJosh Poimboeuf  * the lack of a return instruction.
245dcc914f4SJosh Poimboeuf  */
__dead_end_function(struct objtool_file * file,struct symbol * func,int recursion)2468e25c9f8SJosh Poimboeuf static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
247dcc914f4SJosh Poimboeuf 				int recursion)
248dcc914f4SJosh Poimboeuf {
249dcc914f4SJosh Poimboeuf 	int i;
250dcc914f4SJosh Poimboeuf 	struct instruction *insn;
251dcc914f4SJosh Poimboeuf 	bool empty = true;
252dcc914f4SJosh Poimboeuf 
2536245ce4aSJosh Poimboeuf #define NORETURN(func) __stringify(func),
254dcc914f4SJosh Poimboeuf 	static const char * const global_noreturns[] = {
2556245ce4aSJosh Poimboeuf #include "noreturns.h"
256dcc914f4SJosh Poimboeuf 	};
2576245ce4aSJosh Poimboeuf #undef NORETURN
258dcc914f4SJosh Poimboeuf 
259c9bab22bSJosh Poimboeuf 	if (!func)
260c9bab22bSJosh Poimboeuf 		return false;
261c9bab22bSJosh Poimboeuf 
26256d680ddSMiguel Ojeda 	if (func->bind == STB_GLOBAL || func->bind == STB_WEAK) {
26356d680ddSMiguel Ojeda 		if (is_rust_noreturn(func))
26456d680ddSMiguel Ojeda 			return true;
26556d680ddSMiguel Ojeda 
266dcc914f4SJosh Poimboeuf 		for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
267dcc914f4SJosh Poimboeuf 			if (!strcmp(func->name, global_noreturns[i]))
2688e25c9f8SJosh Poimboeuf 				return true;
26956d680ddSMiguel Ojeda 	}
270dcc914f4SJosh Poimboeuf 
2711c47c875SJosh Poimboeuf 	if (func->bind == STB_WEAK)
2721c47c875SJosh Poimboeuf 		return false;
2731c47c875SJosh Poimboeuf 
27413810435SJosh Poimboeuf 	if (!func->len)
2758e25c9f8SJosh Poimboeuf 		return false;
276dcc914f4SJosh Poimboeuf 
27713810435SJosh Poimboeuf 	insn = find_insn(file, func->sec, func->offset);
2785f6e430fSLinus Torvalds 	if (!insn || !insn_func(insn))
2798e25c9f8SJosh Poimboeuf 		return false;
28013810435SJosh Poimboeuf 
281f0f70adbSPeter Zijlstra 	func_for_each_insn(file, func, insn) {
282dcc914f4SJosh Poimboeuf 		empty = false;
283dcc914f4SJosh Poimboeuf 
284dcc914f4SJosh Poimboeuf 		if (insn->type == INSN_RETURN)
2858e25c9f8SJosh Poimboeuf 			return false;
286dcc914f4SJosh Poimboeuf 	}
287dcc914f4SJosh Poimboeuf 
288dcc914f4SJosh Poimboeuf 	if (empty)
2898e25c9f8SJosh Poimboeuf 		return false;
290dcc914f4SJosh Poimboeuf 
291dcc914f4SJosh Poimboeuf 	/*
292dcc914f4SJosh Poimboeuf 	 * A function can have a sibling call instead of a return.  In that
293dcc914f4SJosh Poimboeuf 	 * case, the function's dead-end status depends on whether the target
294dcc914f4SJosh Poimboeuf 	 * of the sibling call returns.
295dcc914f4SJosh Poimboeuf 	 */
296f0f70adbSPeter Zijlstra 	func_for_each_insn(file, func, insn) {
2970c1ddd33SJosh Poimboeuf 		if (is_sibling_call(insn)) {
298dcc914f4SJosh Poimboeuf 			struct instruction *dest = insn->jump_dest;
299dcc914f4SJosh Poimboeuf 
300dcc914f4SJosh Poimboeuf 			if (!dest)
301dcc914f4SJosh Poimboeuf 				/* sibling call to another file */
3028e25c9f8SJosh Poimboeuf 				return false;
303dcc914f4SJosh Poimboeuf 
30413810435SJosh Poimboeuf 			/* local sibling call */
305dcc914f4SJosh Poimboeuf 			if (recursion == 5) {
3060afd0d9eSJosh Poimboeuf 				/*
3070c1ddd33SJosh Poimboeuf 				 * Infinite recursion: two functions have
3080c1ddd33SJosh Poimboeuf 				 * sibling calls to each other.  This is a very
3090c1ddd33SJosh Poimboeuf 				 * rare case.  It means they aren't dead ends.
3100afd0d9eSJosh Poimboeuf 				 */
3118e25c9f8SJosh Poimboeuf 				return false;
312dcc914f4SJosh Poimboeuf 			}
313dcc914f4SJosh Poimboeuf 
314dbcdbdfdSPeter Zijlstra 			return __dead_end_function(file, insn_func(dest), recursion+1);
315dcc914f4SJosh Poimboeuf 		}
316dcc914f4SJosh Poimboeuf 	}
317dcc914f4SJosh Poimboeuf 
3188e25c9f8SJosh Poimboeuf 	return true;
319dcc914f4SJosh Poimboeuf }
320dcc914f4SJosh Poimboeuf 
dead_end_function(struct objtool_file * file,struct symbol * func)3218e25c9f8SJosh Poimboeuf static bool dead_end_function(struct objtool_file *file, struct symbol *func)
322dcc914f4SJosh Poimboeuf {
323dcc914f4SJosh Poimboeuf 	return __dead_end_function(file, func, 0);
324dcc914f4SJosh Poimboeuf }
325dcc914f4SJosh Poimboeuf 
init_cfi_state(struct cfi_state * cfi)326e7c0219bSPeter Zijlstra static void init_cfi_state(struct cfi_state *cfi)
327baa41469SJosh Poimboeuf {
328baa41469SJosh Poimboeuf 	int i;
329baa41469SJosh Poimboeuf 
330dd88a0a0SJosh Poimboeuf 	for (i = 0; i < CFI_NUM_REGS; i++) {
331e7c0219bSPeter Zijlstra 		cfi->regs[i].base = CFI_UNDEFINED;
332e7c0219bSPeter Zijlstra 		cfi->vals[i].base = CFI_UNDEFINED;
333dd88a0a0SJosh Poimboeuf 	}
334e7c0219bSPeter Zijlstra 	cfi->cfa.base = CFI_UNDEFINED;
335e7c0219bSPeter Zijlstra 	cfi->drap_reg = CFI_UNDEFINED;
336e7c0219bSPeter Zijlstra 	cfi->drap_offset = -1;
337e7c0219bSPeter Zijlstra }
338e7c0219bSPeter Zijlstra 
init_insn_state(struct objtool_file * file,struct insn_state * state,struct section * sec)339753da417SJosh Poimboeuf static void init_insn_state(struct objtool_file *file, struct insn_state *state,
340753da417SJosh Poimboeuf 			    struct section *sec)
341e7c0219bSPeter Zijlstra {
342e7c0219bSPeter Zijlstra 	memset(state, 0, sizeof(*state));
343e7c0219bSPeter Zijlstra 	init_cfi_state(&state->cfi);
344932f8e98SPeter Zijlstra 
345a8d39a62SJosh Poimboeuf 	if (opts.noinstr && sec)
346932f8e98SPeter Zijlstra 		state->noinstr = sec->noinstr;
347baa41469SJosh Poimboeuf }
348baa41469SJosh Poimboeuf 
cfi_alloc(void)3498b946cc3SPeter Zijlstra static struct cfi_state *cfi_alloc(void)
3508b946cc3SPeter Zijlstra {
351e2e13630SSam James 	struct cfi_state *cfi = calloc(1, sizeof(struct cfi_state));
3528b946cc3SPeter Zijlstra 	if (!cfi) {
3533e7be635SJosh Poimboeuf 		ERROR_GLIBC("calloc");
3548b946cc3SPeter Zijlstra 		exit(1);
3558b946cc3SPeter Zijlstra 	}
3568b946cc3SPeter Zijlstra 	nr_cfi++;
3578b946cc3SPeter Zijlstra 	return cfi;
3588b946cc3SPeter Zijlstra }
3598b946cc3SPeter Zijlstra 
3608b946cc3SPeter Zijlstra static int cfi_bits;
3618b946cc3SPeter Zijlstra static struct hlist_head *cfi_hash;
3628b946cc3SPeter Zijlstra 
cficmp(struct cfi_state * cfi1,struct cfi_state * cfi2)3638b946cc3SPeter Zijlstra static inline bool cficmp(struct cfi_state *cfi1, struct cfi_state *cfi2)
3648b946cc3SPeter Zijlstra {
3658b946cc3SPeter Zijlstra 	return memcmp((void *)cfi1 + sizeof(cfi1->hash),
3668b946cc3SPeter Zijlstra 		      (void *)cfi2 + sizeof(cfi2->hash),
3678b946cc3SPeter Zijlstra 		      sizeof(struct cfi_state) - sizeof(struct hlist_node));
3688b946cc3SPeter Zijlstra }
3698b946cc3SPeter Zijlstra 
cfi_key(struct cfi_state * cfi)3708b946cc3SPeter Zijlstra static inline u32 cfi_key(struct cfi_state *cfi)
3718b946cc3SPeter Zijlstra {
3728b946cc3SPeter Zijlstra 	return jhash((void *)cfi + sizeof(cfi->hash),
3738b946cc3SPeter Zijlstra 		     sizeof(*cfi) - sizeof(cfi->hash), 0);
3748b946cc3SPeter Zijlstra }
3758b946cc3SPeter Zijlstra 
cfi_hash_find_or_add(struct cfi_state * cfi)3768b946cc3SPeter Zijlstra static struct cfi_state *cfi_hash_find_or_add(struct cfi_state *cfi)
3778b946cc3SPeter Zijlstra {
3788b946cc3SPeter Zijlstra 	struct hlist_head *head = &cfi_hash[hash_min(cfi_key(cfi), cfi_bits)];
3798b946cc3SPeter Zijlstra 	struct cfi_state *obj;
3808b946cc3SPeter Zijlstra 
3818b946cc3SPeter Zijlstra 	hlist_for_each_entry(obj, head, hash) {
3828b946cc3SPeter Zijlstra 		if (!cficmp(cfi, obj)) {
3838b946cc3SPeter Zijlstra 			nr_cfi_cache++;
3848b946cc3SPeter Zijlstra 			return obj;
3858b946cc3SPeter Zijlstra 		}
3868b946cc3SPeter Zijlstra 	}
3878b946cc3SPeter Zijlstra 
3888b946cc3SPeter Zijlstra 	obj = cfi_alloc();
3898b946cc3SPeter Zijlstra 	*obj = *cfi;
3908b946cc3SPeter Zijlstra 	hlist_add_head(&obj->hash, head);
3918b946cc3SPeter Zijlstra 
3928b946cc3SPeter Zijlstra 	return obj;
3938b946cc3SPeter Zijlstra }
3948b946cc3SPeter Zijlstra 
cfi_hash_add(struct cfi_state * cfi)3958b946cc3SPeter Zijlstra static void cfi_hash_add(struct cfi_state *cfi)
3968b946cc3SPeter Zijlstra {
3978b946cc3SPeter Zijlstra 	struct hlist_head *head = &cfi_hash[hash_min(cfi_key(cfi), cfi_bits)];
3988b946cc3SPeter Zijlstra 
3998b946cc3SPeter Zijlstra 	hlist_add_head(&cfi->hash, head);
4008b946cc3SPeter Zijlstra }
4018b946cc3SPeter Zijlstra 
cfi_hash_alloc(unsigned long size)4028b946cc3SPeter Zijlstra static void *cfi_hash_alloc(unsigned long size)
4038b946cc3SPeter Zijlstra {
4048b946cc3SPeter Zijlstra 	cfi_bits = max(10, ilog2(size));
4058b946cc3SPeter Zijlstra 	cfi_hash = mmap(NULL, sizeof(struct hlist_head) << cfi_bits,
4068b946cc3SPeter Zijlstra 			PROT_READ|PROT_WRITE,
4078b946cc3SPeter Zijlstra 			MAP_PRIVATE|MAP_ANON, -1, 0);
4088b946cc3SPeter Zijlstra 	if (cfi_hash == (void *)-1L) {
4093e7be635SJosh Poimboeuf 		ERROR_GLIBC("mmap fail cfi_hash");
4108b946cc3SPeter Zijlstra 		cfi_hash = NULL;
4112daf7fabSJosh Poimboeuf 	}  else if (opts.stats) {
4128b946cc3SPeter Zijlstra 		printf("cfi_bits: %d\n", cfi_bits);
4138b946cc3SPeter Zijlstra 	}
4148b946cc3SPeter Zijlstra 
4158b946cc3SPeter Zijlstra 	return cfi_hash;
4168b946cc3SPeter Zijlstra }
4178b946cc3SPeter Zijlstra 
4188b946cc3SPeter Zijlstra static unsigned long nr_insns;
4198b946cc3SPeter Zijlstra static unsigned long nr_insns_visited;
4208b946cc3SPeter Zijlstra 
421dcc914f4SJosh Poimboeuf /*
422dcc914f4SJosh Poimboeuf  * Call the arch-specific instruction decoder for all the instructions and add
423dcc914f4SJosh Poimboeuf  * them to the global instruction list.
424dcc914f4SJosh Poimboeuf  */
decode_instructions(struct objtool_file * file)425dcc914f4SJosh Poimboeuf static int decode_instructions(struct objtool_file *file)
426dcc914f4SJosh Poimboeuf {
427dcc914f4SJosh Poimboeuf 	struct section *sec;
428dcc914f4SJosh Poimboeuf 	struct symbol *func;
429dcc914f4SJosh Poimboeuf 	unsigned long offset;
430dcc914f4SJosh Poimboeuf 	struct instruction *insn;
431dcc914f4SJosh Poimboeuf 	int ret;
432dcc914f4SJosh Poimboeuf 
433baa41469SJosh Poimboeuf 	for_each_sec(file, sec) {
4341c34496eSPeter Zijlstra 		struct instruction *insns = NULL;
4351c34496eSPeter Zijlstra 		u8 prev_len = 0;
4361c34496eSPeter Zijlstra 		u8 idx = 0;
437dcc914f4SJosh Poimboeuf 
438dcc914f4SJosh Poimboeuf 		if (!(sec->sh.sh_flags & SHF_EXECINSTR))
439dcc914f4SJosh Poimboeuf 			continue;
440dcc914f4SJosh Poimboeuf 
441627fce14SJosh Poimboeuf 		if (strcmp(sec->name, ".altinstr_replacement") &&
442627fce14SJosh Poimboeuf 		    strcmp(sec->name, ".altinstr_aux") &&
443627fce14SJosh Poimboeuf 		    strncmp(sec->name, ".discard.", 9))
444627fce14SJosh Poimboeuf 			sec->text = true;
445627fce14SJosh Poimboeuf 
4460cc9ac8dSThomas Gleixner 		if (!strcmp(sec->name, ".noinstr.text") ||
447951ddecfSPeter Zijlstra 		    !strcmp(sec->name, ".entry.text") ||
4482b5a0e42SPeter Zijlstra 		    !strcmp(sec->name, ".cpuidle.text") ||
44979cd2a11SPetr Pavlu 		    !strncmp(sec->name, ".text..__x86.", 13))
450c4a33939SPeter Zijlstra 			sec->noinstr = true;
451c4a33939SPeter Zijlstra 
4526644ee84SPeter Zijlstra 		/*
4536644ee84SPeter Zijlstra 		 * .init.text code is ran before userspace and thus doesn't
4546644ee84SPeter Zijlstra 		 * strictly need retpolines, except for modules which are
4556644ee84SPeter Zijlstra 		 * loaded late, they very much do need retpoline in their
4566644ee84SPeter Zijlstra 		 * .init.text
4576644ee84SPeter Zijlstra 		 */
4586644ee84SPeter Zijlstra 		if (!strcmp(sec->name, ".init.text") && !opts.module)
4596644ee84SPeter Zijlstra 			sec->init = true;
4606644ee84SPeter Zijlstra 
461fe255fe6SJoe Lawrence 		for (offset = 0; offset < sec->sh.sh_size; offset += insn->len) {
4621c34496eSPeter Zijlstra 			if (!insns || idx == INSN_CHUNK_MAX) {
4631c34496eSPeter Zijlstra 				insns = calloc(sizeof(*insn), INSN_CHUNK_SIZE);
4641c34496eSPeter Zijlstra 				if (!insns) {
4653e7be635SJosh Poimboeuf 					ERROR_GLIBC("calloc");
466baa41469SJosh Poimboeuf 					return -1;
467baa41469SJosh Poimboeuf 				}
4681c34496eSPeter Zijlstra 				idx = 0;
4691c34496eSPeter Zijlstra 			} else {
4701c34496eSPeter Zijlstra 				idx++;
4711c34496eSPeter Zijlstra 			}
4721c34496eSPeter Zijlstra 			insn = &insns[idx];
4731c34496eSPeter Zijlstra 			insn->idx = idx;
474baa41469SJosh Poimboeuf 
4751c34496eSPeter Zijlstra 			INIT_LIST_HEAD(&insn->call_node);
476dcc914f4SJosh Poimboeuf 			insn->sec = sec;
477dcc914f4SJosh Poimboeuf 			insn->offset = offset;
4781c34496eSPeter Zijlstra 			insn->prev_len = prev_len;
479dcc914f4SJosh Poimboeuf 
480db2b0c5dSPeter Zijlstra 			ret = arch_decode_instruction(file, sec, offset,
481fe255fe6SJoe Lawrence 						      sec->sh.sh_size - offset,
48220a55463SPeter Zijlstra 						      insn);
483dcc914f4SJosh Poimboeuf 			if (ret)
4841c34496eSPeter Zijlstra 				return ret;
4851c34496eSPeter Zijlstra 
4861c34496eSPeter Zijlstra 			prev_len = insn->len;
487dcc914f4SJosh Poimboeuf 
4880e5b613bSPeter Zijlstra 			/*
4890e5b613bSPeter Zijlstra 			 * By default, "ud2" is a dead end unless otherwise
4900e5b613bSPeter Zijlstra 			 * annotated, because GCC 7 inserts it for certain
4910e5b613bSPeter Zijlstra 			 * divide-by-zero cases.
4920e5b613bSPeter Zijlstra 			 */
4930e5b613bSPeter Zijlstra 			if (insn->type == INSN_BUG)
4940e5b613bSPeter Zijlstra 				insn->dead_end = true;
4950e5b613bSPeter Zijlstra 
49687ecb582SPeter Zijlstra 			hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset));
4971e11f3fdSPeter Zijlstra 			nr_insns++;
498dcc914f4SJosh Poimboeuf 		}
499dcc914f4SJosh Poimboeuf 
5009290e772SJosh Poimboeuf 		sec_for_each_sym(sec, func) {
501dbcdbdfdSPeter Zijlstra 			if (func->type != STT_NOTYPE && func->type != STT_FUNC)
502dbcdbdfdSPeter Zijlstra 				continue;
503dbcdbdfdSPeter Zijlstra 
504cad90e53SNicholas Piggin 			if (func->offset == sec->sh.sh_size) {
505cad90e53SNicholas Piggin 				/* Heuristic: likely an "end" symbol */
506cad90e53SNicholas Piggin 				if (func->type == STT_NOTYPE)
507cad90e53SNicholas Piggin 					continue;
5083e7be635SJosh Poimboeuf 				ERROR("%s(): STT_FUNC at end of section", func->name);
509cad90e53SNicholas Piggin 				return -1;
510cad90e53SNicholas Piggin 			}
511cad90e53SNicholas Piggin 
5124ae68b26SPeter Zijlstra 			if (func->embedded_insn || func->alias != func)
513dcc914f4SJosh Poimboeuf 				continue;
514dcc914f4SJosh Poimboeuf 
515dcc914f4SJosh Poimboeuf 			if (!find_insn(file, sec, func->offset)) {
5163e7be635SJosh Poimboeuf 				ERROR("%s(): can't find starting instruction", func->name);
517dcc914f4SJosh Poimboeuf 				return -1;
518dcc914f4SJosh Poimboeuf 			}
519dcc914f4SJosh Poimboeuf 
52008f87a93SPeter Zijlstra 			sym_for_each_insn(file, func, insn) {
521dbcdbdfdSPeter Zijlstra 				insn->sym = func;
522dbcdbdfdSPeter Zijlstra 				if (func->type == STT_FUNC &&
523dbcdbdfdSPeter Zijlstra 				    insn->type == INSN_ENDBR &&
524dbcdbdfdSPeter Zijlstra 				    list_empty(&insn->call_node)) {
525dbcdbdfdSPeter Zijlstra 					if (insn->offset == func->offset) {
52689bc853eSPeter Zijlstra 						list_add_tail(&insn->call_node, &file->endbr_list);
52708f87a93SPeter Zijlstra 						file->nr_endbr++;
52808f87a93SPeter Zijlstra 					} else {
52908f87a93SPeter Zijlstra 						file->nr_endbr_int++;
53008f87a93SPeter Zijlstra 					}
53108f87a93SPeter Zijlstra 				}
53208f87a93SPeter Zijlstra 			}
533dcc914f4SJosh Poimboeuf 		}
534dcc914f4SJosh Poimboeuf 	}
535dcc914f4SJosh Poimboeuf 
5362daf7fabSJosh Poimboeuf 	if (opts.stats)
5371e11f3fdSPeter Zijlstra 		printf("nr_insns: %lu\n", nr_insns);
5381e11f3fdSPeter Zijlstra 
539dcc914f4SJosh Poimboeuf 	return 0;
540dcc914f4SJosh Poimboeuf }
541dcc914f4SJosh Poimboeuf 
542db2b0c5dSPeter Zijlstra /*
543db2b0c5dSPeter Zijlstra  * Read the pv_ops[] .data table to find the static initialized values.
544db2b0c5dSPeter Zijlstra  */
add_pv_ops(struct objtool_file * file,const char * symname)545db2b0c5dSPeter Zijlstra static int add_pv_ops(struct objtool_file *file, const char *symname)
546db2b0c5dSPeter Zijlstra {
547db2b0c5dSPeter Zijlstra 	struct symbol *sym, *func;
548db2b0c5dSPeter Zijlstra 	unsigned long off, end;
549a5bd6236SJosh Poimboeuf 	struct reloc *reloc;
550db2b0c5dSPeter Zijlstra 	int idx;
551db2b0c5dSPeter Zijlstra 
552db2b0c5dSPeter Zijlstra 	sym = find_symbol_by_name(file->elf, symname);
553db2b0c5dSPeter Zijlstra 	if (!sym)
554db2b0c5dSPeter Zijlstra 		return 0;
555db2b0c5dSPeter Zijlstra 
556db2b0c5dSPeter Zijlstra 	off = sym->offset;
557db2b0c5dSPeter Zijlstra 	end = off + sym->len;
558db2b0c5dSPeter Zijlstra 	for (;;) {
559a5bd6236SJosh Poimboeuf 		reloc = find_reloc_by_dest_range(file->elf, sym->sec, off, end - off);
560a5bd6236SJosh Poimboeuf 		if (!reloc)
561db2b0c5dSPeter Zijlstra 			break;
562db2b0c5dSPeter Zijlstra 
563c5995abeSJosh Poimboeuf 		idx = (reloc_offset(reloc) - sym->offset) / sizeof(unsigned long);
564c5995abeSJosh Poimboeuf 
565a5bd6236SJosh Poimboeuf 		func = reloc->sym;
566db2b0c5dSPeter Zijlstra 		if (func->type == STT_SECTION)
5670696b6e3SJosh Poimboeuf 			func = find_symbol_by_offset(reloc->sym->sec,
5680696b6e3SJosh Poimboeuf 						     reloc_addend(reloc));
569c5995abeSJosh Poimboeuf 		if (!func) {
5703e7be635SJosh Poimboeuf 			ERROR_FUNC(reloc->sym->sec, reloc_addend(reloc),
5713e7be635SJosh Poimboeuf 				   "can't find func at %s[%d]", symname, idx);
572c5995abeSJosh Poimboeuf 			return -1;
573c5995abeSJosh Poimboeuf 		}
574db2b0c5dSPeter Zijlstra 
575c5995abeSJosh Poimboeuf 		if (objtool_pv_add(file, idx, func))
576c5995abeSJosh Poimboeuf 			return -1;
577db2b0c5dSPeter Zijlstra 
578e4cbb9b8SJosh Poimboeuf 		off = reloc_offset(reloc) + 1;
579db2b0c5dSPeter Zijlstra 		if (off > end)
580db2b0c5dSPeter Zijlstra 			break;
581db2b0c5dSPeter Zijlstra 	}
582db2b0c5dSPeter Zijlstra 
583db2b0c5dSPeter Zijlstra 	return 0;
584db2b0c5dSPeter Zijlstra }
585db2b0c5dSPeter Zijlstra 
586db2b0c5dSPeter Zijlstra /*
587db2b0c5dSPeter Zijlstra  * Allocate and initialize file->pv_ops[].
588db2b0c5dSPeter Zijlstra  */
init_pv_ops(struct objtool_file * file)589db2b0c5dSPeter Zijlstra static int init_pv_ops(struct objtool_file *file)
590db2b0c5dSPeter Zijlstra {
591db2b0c5dSPeter Zijlstra 	static const char *pv_ops_tables[] = {
592db2b0c5dSPeter Zijlstra 		"pv_ops",
593db2b0c5dSPeter Zijlstra 		"xen_cpu_ops",
594db2b0c5dSPeter Zijlstra 		"xen_irq_ops",
595db2b0c5dSPeter Zijlstra 		"xen_mmu_ops",
596db2b0c5dSPeter Zijlstra 		NULL,
597db2b0c5dSPeter Zijlstra 	};
598db2b0c5dSPeter Zijlstra 	const char *pv_ops;
599db2b0c5dSPeter Zijlstra 	struct symbol *sym;
600c5995abeSJosh Poimboeuf 	int idx, nr, ret;
601db2b0c5dSPeter Zijlstra 
6022daf7fabSJosh Poimboeuf 	if (!opts.noinstr)
603db2b0c5dSPeter Zijlstra 		return 0;
604db2b0c5dSPeter Zijlstra 
605db2b0c5dSPeter Zijlstra 	file->pv_ops = NULL;
606db2b0c5dSPeter Zijlstra 
607db2b0c5dSPeter Zijlstra 	sym = find_symbol_by_name(file->elf, "pv_ops");
608db2b0c5dSPeter Zijlstra 	if (!sym)
609db2b0c5dSPeter Zijlstra 		return 0;
610db2b0c5dSPeter Zijlstra 
611db2b0c5dSPeter Zijlstra 	nr = sym->len / sizeof(unsigned long);
612db2b0c5dSPeter Zijlstra 	file->pv_ops = calloc(sizeof(struct pv_state), nr);
613c5995abeSJosh Poimboeuf 	if (!file->pv_ops) {
6143e7be635SJosh Poimboeuf 		ERROR_GLIBC("calloc");
615db2b0c5dSPeter Zijlstra 		return -1;
616c5995abeSJosh Poimboeuf 	}
617db2b0c5dSPeter Zijlstra 
618db2b0c5dSPeter Zijlstra 	for (idx = 0; idx < nr; idx++)
619db2b0c5dSPeter Zijlstra 		INIT_LIST_HEAD(&file->pv_ops[idx].targets);
620db2b0c5dSPeter Zijlstra 
621c5995abeSJosh Poimboeuf 	for (idx = 0; (pv_ops = pv_ops_tables[idx]); idx++) {
622c5995abeSJosh Poimboeuf 		ret = add_pv_ops(file, pv_ops);
623c5995abeSJosh Poimboeuf 		if (ret)
624c5995abeSJosh Poimboeuf 			return ret;
625c5995abeSJosh Poimboeuf 	}
626db2b0c5dSPeter Zijlstra 
627db2b0c5dSPeter Zijlstra 	return 0;
628db2b0c5dSPeter Zijlstra }
629db2b0c5dSPeter Zijlstra 
create_static_call_sections(struct objtool_file * file)6301e7e4788SJosh Poimboeuf static int create_static_call_sections(struct objtool_file *file)
6311e7e4788SJosh Poimboeuf {
6321e7e4788SJosh Poimboeuf 	struct static_call_site *site;
6336342a20eSJosh Poimboeuf 	struct section *sec;
6341e7e4788SJosh Poimboeuf 	struct instruction *insn;
6351e7e4788SJosh Poimboeuf 	struct symbol *key_sym;
6361e7e4788SJosh Poimboeuf 	char *key_name, *tmp;
6371e7e4788SJosh Poimboeuf 	int idx;
6381e7e4788SJosh Poimboeuf 
6391e7e4788SJosh Poimboeuf 	sec = find_section_by_name(file->elf, ".static_call_sites");
6401e7e4788SJosh Poimboeuf 	if (sec) {
6411e7e4788SJosh Poimboeuf 		INIT_LIST_HEAD(&file->static_call_list);
6421e7e4788SJosh Poimboeuf 		WARN("file already has .static_call_sites section, skipping");
6431e7e4788SJosh Poimboeuf 		return 0;
6441e7e4788SJosh Poimboeuf 	}
6451e7e4788SJosh Poimboeuf 
6461e7e4788SJosh Poimboeuf 	if (list_empty(&file->static_call_list))
6471e7e4788SJosh Poimboeuf 		return 0;
6481e7e4788SJosh Poimboeuf 
6491e7e4788SJosh Poimboeuf 	idx = 0;
65043d5430aSPeter Zijlstra 	list_for_each_entry(insn, &file->static_call_list, call_node)
6511e7e4788SJosh Poimboeuf 		idx++;
6521e7e4788SJosh Poimboeuf 
6536342a20eSJosh Poimboeuf 	sec = elf_create_section_pair(file->elf, ".static_call_sites",
6546342a20eSJosh Poimboeuf 				      sizeof(*site), idx, idx * 2);
6551e7e4788SJosh Poimboeuf 	if (!sec)
6561e7e4788SJosh Poimboeuf 		return -1;
6571e7e4788SJosh Poimboeuf 
6586342a20eSJosh Poimboeuf 	/* Allow modules to modify the low bits of static_call_site::key */
6592707579dSJosh Poimboeuf 	sec->sh.sh_flags |= SHF_WRITE;
6602707579dSJosh Poimboeuf 
6611e7e4788SJosh Poimboeuf 	idx = 0;
66243d5430aSPeter Zijlstra 	list_for_each_entry(insn, &file->static_call_list, call_node) {
6631e7e4788SJosh Poimboeuf 
6641e7e4788SJosh Poimboeuf 		/* populate reloc for 'addr' */
6656342a20eSJosh Poimboeuf 		if (!elf_init_reloc_text_sym(file->elf, sec,
6666342a20eSJosh Poimboeuf 					     idx * sizeof(*site), idx * 2,
667ef47cc01SPeter Zijlstra 					     insn->sec, insn->offset))
6681e7e4788SJosh Poimboeuf 			return -1;
6691e7e4788SJosh Poimboeuf 
6701e7e4788SJosh Poimboeuf 		/* find key symbol */
671c6f5dc28SPeter Zijlstra 		key_name = strdup(insn_call_dest(insn)->name);
6721e7e4788SJosh Poimboeuf 		if (!key_name) {
6733e7be635SJosh Poimboeuf 			ERROR_GLIBC("strdup");
6741e7e4788SJosh Poimboeuf 			return -1;
6751e7e4788SJosh Poimboeuf 		}
6761e7e4788SJosh Poimboeuf 		if (strncmp(key_name, STATIC_CALL_TRAMP_PREFIX_STR,
6771e7e4788SJosh Poimboeuf 			    STATIC_CALL_TRAMP_PREFIX_LEN)) {
6783e7be635SJosh Poimboeuf 			ERROR("static_call: trampoline name malformed: %s", key_name);
6791e7e4788SJosh Poimboeuf 			return -1;
6801e7e4788SJosh Poimboeuf 		}
6811e7e4788SJosh Poimboeuf 		tmp = key_name + STATIC_CALL_TRAMP_PREFIX_LEN - STATIC_CALL_KEY_PREFIX_LEN;
6821e7e4788SJosh Poimboeuf 		memcpy(tmp, STATIC_CALL_KEY_PREFIX_STR, STATIC_CALL_KEY_PREFIX_LEN);
6831e7e4788SJosh Poimboeuf 
6841e7e4788SJosh Poimboeuf 		key_sym = find_symbol_by_name(file->elf, tmp);
6851e7e4788SJosh Poimboeuf 		if (!key_sym) {
6862daf7fabSJosh Poimboeuf 			if (!opts.module) {
6873e7be635SJosh Poimboeuf 				ERROR("static_call: can't find static_call_key symbol: %s", tmp);
6881e7e4788SJosh Poimboeuf 				return -1;
6891e7e4788SJosh Poimboeuf 			}
69073f44fe1SJosh Poimboeuf 
69173f44fe1SJosh Poimboeuf 			/*
69273f44fe1SJosh Poimboeuf 			 * For modules(), the key might not be exported, which
69373f44fe1SJosh Poimboeuf 			 * means the module can make static calls but isn't
69473f44fe1SJosh Poimboeuf 			 * allowed to change them.
69573f44fe1SJosh Poimboeuf 			 *
69673f44fe1SJosh Poimboeuf 			 * In that case we temporarily set the key to be the
69773f44fe1SJosh Poimboeuf 			 * trampoline address.  This is fixed up in
69873f44fe1SJosh Poimboeuf 			 * static_call_add_module().
69973f44fe1SJosh Poimboeuf 			 */
700c6f5dc28SPeter Zijlstra 			key_sym = insn_call_dest(insn);
70173f44fe1SJosh Poimboeuf 		}
7021e7e4788SJosh Poimboeuf 
7031e7e4788SJosh Poimboeuf 		/* populate reloc for 'key' */
7046342a20eSJosh Poimboeuf 		if (!elf_init_reloc_data_sym(file->elf, sec,
7056342a20eSJosh Poimboeuf 					     idx * sizeof(*site) + 4,
7066342a20eSJosh Poimboeuf 					     (idx * 2) + 1, key_sym,
707ef47cc01SPeter Zijlstra 					     is_sibling_call(insn) * STATIC_CALL_SITE_TAIL))
7081e7e4788SJosh Poimboeuf 			return -1;
7091e7e4788SJosh Poimboeuf 
7101e7e4788SJosh Poimboeuf 		idx++;
7111e7e4788SJosh Poimboeuf 	}
7121e7e4788SJosh Poimboeuf 
7131e7e4788SJosh Poimboeuf 	return 0;
7141e7e4788SJosh Poimboeuf }
7151e7e4788SJosh Poimboeuf 
create_retpoline_sites_sections(struct objtool_file * file)716134ab5bdSPeter Zijlstra static int create_retpoline_sites_sections(struct objtool_file *file)
717134ab5bdSPeter Zijlstra {
718134ab5bdSPeter Zijlstra 	struct instruction *insn;
719134ab5bdSPeter Zijlstra 	struct section *sec;
720134ab5bdSPeter Zijlstra 	int idx;
721134ab5bdSPeter Zijlstra 
722134ab5bdSPeter Zijlstra 	sec = find_section_by_name(file->elf, ".retpoline_sites");
723134ab5bdSPeter Zijlstra 	if (sec) {
724134ab5bdSPeter Zijlstra 		WARN("file already has .retpoline_sites, skipping");
725134ab5bdSPeter Zijlstra 		return 0;
726134ab5bdSPeter Zijlstra 	}
727134ab5bdSPeter Zijlstra 
728134ab5bdSPeter Zijlstra 	idx = 0;
729134ab5bdSPeter Zijlstra 	list_for_each_entry(insn, &file->retpoline_call_list, call_node)
730134ab5bdSPeter Zijlstra 		idx++;
731134ab5bdSPeter Zijlstra 
732134ab5bdSPeter Zijlstra 	if (!idx)
733134ab5bdSPeter Zijlstra 		return 0;
734134ab5bdSPeter Zijlstra 
7356342a20eSJosh Poimboeuf 	sec = elf_create_section_pair(file->elf, ".retpoline_sites",
7366342a20eSJosh Poimboeuf 				      sizeof(int), idx, idx);
7376342a20eSJosh Poimboeuf 	if (!sec)
738134ab5bdSPeter Zijlstra 		return -1;
739134ab5bdSPeter Zijlstra 
740134ab5bdSPeter Zijlstra 	idx = 0;
741134ab5bdSPeter Zijlstra 	list_for_each_entry(insn, &file->retpoline_call_list, call_node) {
742134ab5bdSPeter Zijlstra 
7436342a20eSJosh Poimboeuf 		if (!elf_init_reloc_text_sym(file->elf, sec,
7446342a20eSJosh Poimboeuf 					     idx * sizeof(int), idx,
7456342a20eSJosh Poimboeuf 					     insn->sec, insn->offset))
746134ab5bdSPeter Zijlstra 			return -1;
747134ab5bdSPeter Zijlstra 
748134ab5bdSPeter Zijlstra 		idx++;
749134ab5bdSPeter Zijlstra 	}
750134ab5bdSPeter Zijlstra 
751134ab5bdSPeter Zijlstra 	return 0;
752134ab5bdSPeter Zijlstra }
753134ab5bdSPeter Zijlstra 
create_return_sites_sections(struct objtool_file * file)754d9e9d230SPeter Zijlstra static int create_return_sites_sections(struct objtool_file *file)
755d9e9d230SPeter Zijlstra {
756d9e9d230SPeter Zijlstra 	struct instruction *insn;
757d9e9d230SPeter Zijlstra 	struct section *sec;
758d9e9d230SPeter Zijlstra 	int idx;
759d9e9d230SPeter Zijlstra 
760d9e9d230SPeter Zijlstra 	sec = find_section_by_name(file->elf, ".return_sites");
761d9e9d230SPeter Zijlstra 	if (sec) {
762d9e9d230SPeter Zijlstra 		WARN("file already has .return_sites, skipping");
763d9e9d230SPeter Zijlstra 		return 0;
764d9e9d230SPeter Zijlstra 	}
765d9e9d230SPeter Zijlstra 
766d9e9d230SPeter Zijlstra 	idx = 0;
767d9e9d230SPeter Zijlstra 	list_for_each_entry(insn, &file->return_thunk_list, call_node)
768d9e9d230SPeter Zijlstra 		idx++;
769d9e9d230SPeter Zijlstra 
770d9e9d230SPeter Zijlstra 	if (!idx)
771d9e9d230SPeter Zijlstra 		return 0;
772d9e9d230SPeter Zijlstra 
7736342a20eSJosh Poimboeuf 	sec = elf_create_section_pair(file->elf, ".return_sites",
7746342a20eSJosh Poimboeuf 				      sizeof(int), idx, idx);
7756342a20eSJosh Poimboeuf 	if (!sec)
776d9e9d230SPeter Zijlstra 		return -1;
777d9e9d230SPeter Zijlstra 
778d9e9d230SPeter Zijlstra 	idx = 0;
779d9e9d230SPeter Zijlstra 	list_for_each_entry(insn, &file->return_thunk_list, call_node) {
780d9e9d230SPeter Zijlstra 
7816342a20eSJosh Poimboeuf 		if (!elf_init_reloc_text_sym(file->elf, sec,
7826342a20eSJosh Poimboeuf 					     idx * sizeof(int), idx,
7836342a20eSJosh Poimboeuf 					     insn->sec, insn->offset))
784d9e9d230SPeter Zijlstra 			return -1;
785d9e9d230SPeter Zijlstra 
786d9e9d230SPeter Zijlstra 		idx++;
787d9e9d230SPeter Zijlstra 	}
788d9e9d230SPeter Zijlstra 
789d9e9d230SPeter Zijlstra 	return 0;
790d9e9d230SPeter Zijlstra }
791d9e9d230SPeter Zijlstra 
create_ibt_endbr_seal_sections(struct objtool_file * file)79289bc853eSPeter Zijlstra static int create_ibt_endbr_seal_sections(struct objtool_file *file)
79389bc853eSPeter Zijlstra {
79489bc853eSPeter Zijlstra 	struct instruction *insn;
79589bc853eSPeter Zijlstra 	struct section *sec;
79689bc853eSPeter Zijlstra 	int idx;
79789bc853eSPeter Zijlstra 
79889bc853eSPeter Zijlstra 	sec = find_section_by_name(file->elf, ".ibt_endbr_seal");
79989bc853eSPeter Zijlstra 	if (sec) {
80089bc853eSPeter Zijlstra 		WARN("file already has .ibt_endbr_seal, skipping");
80189bc853eSPeter Zijlstra 		return 0;
80289bc853eSPeter Zijlstra 	}
80389bc853eSPeter Zijlstra 
80489bc853eSPeter Zijlstra 	idx = 0;
80589bc853eSPeter Zijlstra 	list_for_each_entry(insn, &file->endbr_list, call_node)
80689bc853eSPeter Zijlstra 		idx++;
80789bc853eSPeter Zijlstra 
8082daf7fabSJosh Poimboeuf 	if (opts.stats) {
80989bc853eSPeter Zijlstra 		printf("ibt: ENDBR at function start: %d\n", file->nr_endbr);
81089bc853eSPeter Zijlstra 		printf("ibt: ENDBR inside functions:  %d\n", file->nr_endbr_int);
81189bc853eSPeter Zijlstra 		printf("ibt: superfluous ENDBR:       %d\n", idx);
81289bc853eSPeter Zijlstra 	}
81389bc853eSPeter Zijlstra 
81489bc853eSPeter Zijlstra 	if (!idx)
81589bc853eSPeter Zijlstra 		return 0;
81689bc853eSPeter Zijlstra 
8176342a20eSJosh Poimboeuf 	sec = elf_create_section_pair(file->elf, ".ibt_endbr_seal",
8186342a20eSJosh Poimboeuf 				      sizeof(int), idx, idx);
8196342a20eSJosh Poimboeuf 	if (!sec)
82089bc853eSPeter Zijlstra 		return -1;
82189bc853eSPeter Zijlstra 
82289bc853eSPeter Zijlstra 	idx = 0;
82389bc853eSPeter Zijlstra 	list_for_each_entry(insn, &file->endbr_list, call_node) {
82489bc853eSPeter Zijlstra 
82589bc853eSPeter Zijlstra 		int *site = (int *)sec->data->d_buf + idx;
82603d7a105SMichal Kubecek 		struct symbol *sym = insn->sym;
82789bc853eSPeter Zijlstra 		*site = 0;
82889bc853eSPeter Zijlstra 
82903d7a105SMichal Kubecek 		if (opts.module && sym && sym->type == STT_FUNC &&
83003d7a105SMichal Kubecek 		    insn->offset == sym->offset &&
83103d7a105SMichal Kubecek 		    (!strcmp(sym->name, "init_module") ||
8324fab2d76SJosh Poimboeuf 		     !strcmp(sym->name, "cleanup_module"))) {
8333e7be635SJosh Poimboeuf 			ERROR("%s(): Magic init_module() function name is deprecated, use module_init(fn) instead",
8344fab2d76SJosh Poimboeuf 			      sym->name);
8354fab2d76SJosh Poimboeuf 			return -1;
8364fab2d76SJosh Poimboeuf 		}
83703d7a105SMichal Kubecek 
8386342a20eSJosh Poimboeuf 		if (!elf_init_reloc_text_sym(file->elf, sec,
8396342a20eSJosh Poimboeuf 					     idx * sizeof(int), idx,
8406342a20eSJosh Poimboeuf 					     insn->sec, insn->offset))
84189bc853eSPeter Zijlstra 			return -1;
84289bc853eSPeter Zijlstra 
84389bc853eSPeter Zijlstra 		idx++;
84489bc853eSPeter Zijlstra 	}
84589bc853eSPeter Zijlstra 
84689bc853eSPeter Zijlstra 	return 0;
84789bc853eSPeter Zijlstra }
84889bc853eSPeter Zijlstra 
create_cfi_sections(struct objtool_file * file)8499a479f76SPeter Zijlstra static int create_cfi_sections(struct objtool_file *file)
8509a479f76SPeter Zijlstra {
8519290e772SJosh Poimboeuf 	struct section *sec;
8529a479f76SPeter Zijlstra 	struct symbol *sym;
8539a479f76SPeter Zijlstra 	int idx;
8549a479f76SPeter Zijlstra 
8559a479f76SPeter Zijlstra 	sec = find_section_by_name(file->elf, ".cfi_sites");
8569a479f76SPeter Zijlstra 	if (sec) {
8579a479f76SPeter Zijlstra 		INIT_LIST_HEAD(&file->call_list);
8589a479f76SPeter Zijlstra 		WARN("file already has .cfi_sites section, skipping");
8599a479f76SPeter Zijlstra 		return 0;
8609a479f76SPeter Zijlstra 	}
8619a479f76SPeter Zijlstra 
8629a479f76SPeter Zijlstra 	idx = 0;
8639290e772SJosh Poimboeuf 	for_each_sym(file, sym) {
8649a479f76SPeter Zijlstra 		if (sym->type != STT_FUNC)
8659a479f76SPeter Zijlstra 			continue;
8669a479f76SPeter Zijlstra 
8679a479f76SPeter Zijlstra 		if (strncmp(sym->name, "__cfi_", 6))
8689a479f76SPeter Zijlstra 			continue;
8699a479f76SPeter Zijlstra 
8709a479f76SPeter Zijlstra 		idx++;
8719a479f76SPeter Zijlstra 	}
8729a479f76SPeter Zijlstra 
8736342a20eSJosh Poimboeuf 	sec = elf_create_section_pair(file->elf, ".cfi_sites",
8746342a20eSJosh Poimboeuf 				      sizeof(unsigned int), idx, idx);
8759a479f76SPeter Zijlstra 	if (!sec)
8769a479f76SPeter Zijlstra 		return -1;
8779a479f76SPeter Zijlstra 
8789a479f76SPeter Zijlstra 	idx = 0;
8799290e772SJosh Poimboeuf 	for_each_sym(file, sym) {
8809a479f76SPeter Zijlstra 		if (sym->type != STT_FUNC)
8819a479f76SPeter Zijlstra 			continue;
8829a479f76SPeter Zijlstra 
8839a479f76SPeter Zijlstra 		if (strncmp(sym->name, "__cfi_", 6))
8849a479f76SPeter Zijlstra 			continue;
8859a479f76SPeter Zijlstra 
8866342a20eSJosh Poimboeuf 		if (!elf_init_reloc_text_sym(file->elf, sec,
8876342a20eSJosh Poimboeuf 					     idx * sizeof(unsigned int), idx,
8889290e772SJosh Poimboeuf 					     sym->sec, sym->offset))
8899a479f76SPeter Zijlstra 			return -1;
8909a479f76SPeter Zijlstra 
8919a479f76SPeter Zijlstra 		idx++;
8929a479f76SPeter Zijlstra 	}
8939a479f76SPeter Zijlstra 
8949a479f76SPeter Zijlstra 	return 0;
8959a479f76SPeter Zijlstra }
8969a479f76SPeter Zijlstra 
create_mcount_loc_sections(struct objtool_file * file)89799d00215SPeter Zijlstra static int create_mcount_loc_sections(struct objtool_file *file)
89899d00215SPeter Zijlstra {
89953257a97SJosh Poimboeuf 	size_t addr_size = elf_addr_size(file->elf);
90099d00215SPeter Zijlstra 	struct instruction *insn;
90186ea7f36SChristophe Leroy 	struct section *sec;
90299d00215SPeter Zijlstra 	int idx;
90399d00215SPeter Zijlstra 
90499d00215SPeter Zijlstra 	sec = find_section_by_name(file->elf, "__mcount_loc");
90599d00215SPeter Zijlstra 	if (sec) {
90699d00215SPeter Zijlstra 		INIT_LIST_HEAD(&file->mcount_loc_list);
90799d00215SPeter Zijlstra 		WARN("file already has __mcount_loc section, skipping");
90899d00215SPeter Zijlstra 		return 0;
90999d00215SPeter Zijlstra 	}
91099d00215SPeter Zijlstra 
91199d00215SPeter Zijlstra 	if (list_empty(&file->mcount_loc_list))
91299d00215SPeter Zijlstra 		return 0;
91399d00215SPeter Zijlstra 
91499d00215SPeter Zijlstra 	idx = 0;
915c509331bSPeter Zijlstra 	list_for_each_entry(insn, &file->mcount_loc_list, call_node)
91699d00215SPeter Zijlstra 		idx++;
91799d00215SPeter Zijlstra 
9186342a20eSJosh Poimboeuf 	sec = elf_create_section_pair(file->elf, "__mcount_loc", addr_size,
9196342a20eSJosh Poimboeuf 				      idx, idx);
92099d00215SPeter Zijlstra 	if (!sec)
92199d00215SPeter Zijlstra 		return -1;
92299d00215SPeter Zijlstra 
92353257a97SJosh Poimboeuf 	sec->sh.sh_addralign = addr_size;
92486ea7f36SChristophe Leroy 
92599d00215SPeter Zijlstra 	idx = 0;
926c509331bSPeter Zijlstra 	list_for_each_entry(insn, &file->mcount_loc_list, call_node) {
92799d00215SPeter Zijlstra 
9286342a20eSJosh Poimboeuf 		struct reloc *reloc;
92999d00215SPeter Zijlstra 
9306342a20eSJosh Poimboeuf 		reloc = elf_init_reloc_text_sym(file->elf, sec, idx * addr_size, idx,
9316342a20eSJosh Poimboeuf 					       insn->sec, insn->offset);
9326342a20eSJosh Poimboeuf 		if (!reloc)
93399d00215SPeter Zijlstra 			return -1;
93499d00215SPeter Zijlstra 
935ec24b927SJosh Poimboeuf 		set_reloc_type(file->elf, reloc, addr_size == 8 ? R_ABS64 : R_ABS32);
9366342a20eSJosh Poimboeuf 
9376342a20eSJosh Poimboeuf 		idx++;
93899d00215SPeter Zijlstra 	}
93999d00215SPeter Zijlstra 
94099d00215SPeter Zijlstra 	return 0;
94199d00215SPeter Zijlstra }
94299d00215SPeter Zijlstra 
create_direct_call_sections(struct objtool_file * file)94300abd384SPeter Zijlstra static int create_direct_call_sections(struct objtool_file *file)
94400abd384SPeter Zijlstra {
94500abd384SPeter Zijlstra 	struct instruction *insn;
94600abd384SPeter Zijlstra 	struct section *sec;
94700abd384SPeter Zijlstra 	int idx;
94800abd384SPeter Zijlstra 
94900abd384SPeter Zijlstra 	sec = find_section_by_name(file->elf, ".call_sites");
95000abd384SPeter Zijlstra 	if (sec) {
95100abd384SPeter Zijlstra 		INIT_LIST_HEAD(&file->call_list);
95200abd384SPeter Zijlstra 		WARN("file already has .call_sites section, skipping");
95300abd384SPeter Zijlstra 		return 0;
95400abd384SPeter Zijlstra 	}
95500abd384SPeter Zijlstra 
95600abd384SPeter Zijlstra 	if (list_empty(&file->call_list))
95700abd384SPeter Zijlstra 		return 0;
95800abd384SPeter Zijlstra 
95900abd384SPeter Zijlstra 	idx = 0;
96000abd384SPeter Zijlstra 	list_for_each_entry(insn, &file->call_list, call_node)
96100abd384SPeter Zijlstra 		idx++;
96200abd384SPeter Zijlstra 
9636342a20eSJosh Poimboeuf 	sec = elf_create_section_pair(file->elf, ".call_sites",
9646342a20eSJosh Poimboeuf 				      sizeof(unsigned int), idx, idx);
96500abd384SPeter Zijlstra 	if (!sec)
96600abd384SPeter Zijlstra 		return -1;
96700abd384SPeter Zijlstra 
96800abd384SPeter Zijlstra 	idx = 0;
96900abd384SPeter Zijlstra 	list_for_each_entry(insn, &file->call_list, call_node) {
97000abd384SPeter Zijlstra 
9716342a20eSJosh Poimboeuf 		if (!elf_init_reloc_text_sym(file->elf, sec,
9726342a20eSJosh Poimboeuf 					     idx * sizeof(unsigned int), idx,
97300abd384SPeter Zijlstra 					     insn->sec, insn->offset))
97400abd384SPeter Zijlstra 			return -1;
97500abd384SPeter Zijlstra 
97600abd384SPeter Zijlstra 		idx++;
97700abd384SPeter Zijlstra 	}
97800abd384SPeter Zijlstra 
97900abd384SPeter Zijlstra 	return 0;
98000abd384SPeter Zijlstra }
98100abd384SPeter Zijlstra 
982dcc914f4SJosh Poimboeuf /*
983dcc914f4SJosh Poimboeuf  * Warnings shouldn't be reported for ignored functions.
984dcc914f4SJosh Poimboeuf  */
add_ignores(struct objtool_file * file)985c5995abeSJosh Poimboeuf static int add_ignores(struct objtool_file *file)
986dcc914f4SJosh Poimboeuf {
987a5bd6236SJosh Poimboeuf 	struct section *rsec;
988dcc914f4SJosh Poimboeuf 	struct symbol *func;
989f1974222SMatt Helsley 	struct reloc *reloc;
990dcc914f4SJosh Poimboeuf 
991a5bd6236SJosh Poimboeuf 	rsec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard");
992a5bd6236SJosh Poimboeuf 	if (!rsec)
993c5995abeSJosh Poimboeuf 		return 0;
994dcc914f4SJosh Poimboeuf 
995caa4a6b7SJosh Poimboeuf 	for_each_reloc(rsec, reloc) {
996f1974222SMatt Helsley 		switch (reloc->sym->type) {
997aaf5c623SPeter Zijlstra 		case STT_FUNC:
998f1974222SMatt Helsley 			func = reloc->sym;
999aaf5c623SPeter Zijlstra 			break;
1000aaf5c623SPeter Zijlstra 
1001aaf5c623SPeter Zijlstra 		case STT_SECTION:
10020696b6e3SJosh Poimboeuf 			func = find_func_by_offset(reloc->sym->sec, reloc_addend(reloc));
10037acfe531SJosh Poimboeuf 			if (!func)
1004dcc914f4SJosh Poimboeuf 				continue;
1005aaf5c623SPeter Zijlstra 			break;
1006aaf5c623SPeter Zijlstra 
1007aaf5c623SPeter Zijlstra 		default:
10083e7be635SJosh Poimboeuf 			ERROR("unexpected relocation symbol type in %s: %d",
1009a5bd6236SJosh Poimboeuf 			      rsec->name, reloc->sym->type);
1010c5995abeSJosh Poimboeuf 			return -1;
1011aaf5c623SPeter Zijlstra 		}
1012dcc914f4SJosh Poimboeuf 
1013c84301d7SJosh Poimboeuf 		func->ignore = true;
1014b5e2cc57SJosh Poimboeuf 		if (func->cfunc)
1015b5e2cc57SJosh Poimboeuf 			func->cfunc->ignore = true;
1016dcc914f4SJosh Poimboeuf 	}
1017c5995abeSJosh Poimboeuf 
1018c5995abeSJosh Poimboeuf 	return 0;
1019dcc914f4SJosh Poimboeuf }
1020dcc914f4SJosh Poimboeuf 
1021dcc914f4SJosh Poimboeuf /*
1022ea24213dSPeter Zijlstra  * This is a whitelist of functions that is allowed to be called with AC set.
1023ea24213dSPeter Zijlstra  * The list is meant to be minimal and only contains compiler instrumentation
1024ea24213dSPeter Zijlstra  * ABI and a few functions used to implement *_{to,from}_user() functions.
1025ea24213dSPeter Zijlstra  *
1026ea24213dSPeter Zijlstra  * These functions must not directly change AC, but may PUSHF/POPF.
1027ea24213dSPeter Zijlstra  */
1028ea24213dSPeter Zijlstra static const char *uaccess_safe_builtin[] = {
1029ea24213dSPeter Zijlstra 	/* KASAN */
1030ea24213dSPeter Zijlstra 	"kasan_report",
1031f00748bfSAndrey Konovalov 	"kasan_check_range",
1032ea24213dSPeter Zijlstra 	/* KASAN out-of-line */
1033ea24213dSPeter Zijlstra 	"__asan_loadN_noabort",
1034ea24213dSPeter Zijlstra 	"__asan_load1_noabort",
1035ea24213dSPeter Zijlstra 	"__asan_load2_noabort",
1036ea24213dSPeter Zijlstra 	"__asan_load4_noabort",
1037ea24213dSPeter Zijlstra 	"__asan_load8_noabort",
1038ea24213dSPeter Zijlstra 	"__asan_load16_noabort",
1039ea24213dSPeter Zijlstra 	"__asan_storeN_noabort",
1040ea24213dSPeter Zijlstra 	"__asan_store1_noabort",
1041ea24213dSPeter Zijlstra 	"__asan_store2_noabort",
1042ea24213dSPeter Zijlstra 	"__asan_store4_noabort",
1043ea24213dSPeter Zijlstra 	"__asan_store8_noabort",
1044ea24213dSPeter Zijlstra 	"__asan_store16_noabort",
1045b0b8e56bSJann Horn 	"__kasan_check_read",
1046b0b8e56bSJann Horn 	"__kasan_check_write",
1047ea24213dSPeter Zijlstra 	/* KASAN in-line */
1048ea24213dSPeter Zijlstra 	"__asan_report_load_n_noabort",
1049ea24213dSPeter Zijlstra 	"__asan_report_load1_noabort",
1050ea24213dSPeter Zijlstra 	"__asan_report_load2_noabort",
1051ea24213dSPeter Zijlstra 	"__asan_report_load4_noabort",
1052ea24213dSPeter Zijlstra 	"__asan_report_load8_noabort",
1053ea24213dSPeter Zijlstra 	"__asan_report_load16_noabort",
1054ea24213dSPeter Zijlstra 	"__asan_report_store_n_noabort",
1055ea24213dSPeter Zijlstra 	"__asan_report_store1_noabort",
1056ea24213dSPeter Zijlstra 	"__asan_report_store2_noabort",
1057ea24213dSPeter Zijlstra 	"__asan_report_store4_noabort",
1058ea24213dSPeter Zijlstra 	"__asan_report_store8_noabort",
1059ea24213dSPeter Zijlstra 	"__asan_report_store16_noabort",
10605f5c9712SMarco Elver 	/* KCSAN */
10619967683cSMarco Elver 	"__kcsan_check_access",
10620525bd82SMarco Elver 	"__kcsan_mb",
10630525bd82SMarco Elver 	"__kcsan_wmb",
10640525bd82SMarco Elver 	"__kcsan_rmb",
10650525bd82SMarco Elver 	"__kcsan_release",
10665f5c9712SMarco Elver 	"kcsan_found_watchpoint",
10675f5c9712SMarco Elver 	"kcsan_setup_watchpoint",
10689967683cSMarco Elver 	"kcsan_check_scoped_accesses",
106950a19ad4SMarco Elver 	"kcsan_disable_current",
107050a19ad4SMarco Elver 	"kcsan_enable_current_nowarn",
10715f5c9712SMarco Elver 	/* KCSAN/TSAN */
10725f5c9712SMarco Elver 	"__tsan_func_entry",
10735f5c9712SMarco Elver 	"__tsan_func_exit",
10745f5c9712SMarco Elver 	"__tsan_read_range",
10755f5c9712SMarco Elver 	"__tsan_write_range",
10765f5c9712SMarco Elver 	"__tsan_read1",
10775f5c9712SMarco Elver 	"__tsan_read2",
10785f5c9712SMarco Elver 	"__tsan_read4",
10795f5c9712SMarco Elver 	"__tsan_read8",
10805f5c9712SMarco Elver 	"__tsan_read16",
10815f5c9712SMarco Elver 	"__tsan_write1",
10825f5c9712SMarco Elver 	"__tsan_write2",
10835f5c9712SMarco Elver 	"__tsan_write4",
10845f5c9712SMarco Elver 	"__tsan_write8",
10855f5c9712SMarco Elver 	"__tsan_write16",
1086a81b3759SMarco Elver 	"__tsan_read_write1",
1087a81b3759SMarco Elver 	"__tsan_read_write2",
1088a81b3759SMarco Elver 	"__tsan_read_write4",
1089a81b3759SMarco Elver 	"__tsan_read_write8",
1090a81b3759SMarco Elver 	"__tsan_read_write16",
109163646fcbSMarco Elver 	"__tsan_volatile_read1",
109263646fcbSMarco Elver 	"__tsan_volatile_read2",
109363646fcbSMarco Elver 	"__tsan_volatile_read4",
109463646fcbSMarco Elver 	"__tsan_volatile_read8",
109563646fcbSMarco Elver 	"__tsan_volatile_read16",
109663646fcbSMarco Elver 	"__tsan_volatile_write1",
109763646fcbSMarco Elver 	"__tsan_volatile_write2",
109863646fcbSMarco Elver 	"__tsan_volatile_write4",
109963646fcbSMarco Elver 	"__tsan_volatile_write8",
110063646fcbSMarco Elver 	"__tsan_volatile_write16",
1101883957b1SMarco Elver 	"__tsan_atomic8_load",
1102883957b1SMarco Elver 	"__tsan_atomic16_load",
1103883957b1SMarco Elver 	"__tsan_atomic32_load",
1104883957b1SMarco Elver 	"__tsan_atomic64_load",
1105883957b1SMarco Elver 	"__tsan_atomic8_store",
1106883957b1SMarco Elver 	"__tsan_atomic16_store",
1107883957b1SMarco Elver 	"__tsan_atomic32_store",
1108883957b1SMarco Elver 	"__tsan_atomic64_store",
1109883957b1SMarco Elver 	"__tsan_atomic8_exchange",
1110883957b1SMarco Elver 	"__tsan_atomic16_exchange",
1111883957b1SMarco Elver 	"__tsan_atomic32_exchange",
1112883957b1SMarco Elver 	"__tsan_atomic64_exchange",
1113883957b1SMarco Elver 	"__tsan_atomic8_fetch_add",
1114883957b1SMarco Elver 	"__tsan_atomic16_fetch_add",
1115883957b1SMarco Elver 	"__tsan_atomic32_fetch_add",
1116883957b1SMarco Elver 	"__tsan_atomic64_fetch_add",
1117883957b1SMarco Elver 	"__tsan_atomic8_fetch_sub",
1118883957b1SMarco Elver 	"__tsan_atomic16_fetch_sub",
1119883957b1SMarco Elver 	"__tsan_atomic32_fetch_sub",
1120883957b1SMarco Elver 	"__tsan_atomic64_fetch_sub",
1121883957b1SMarco Elver 	"__tsan_atomic8_fetch_and",
1122883957b1SMarco Elver 	"__tsan_atomic16_fetch_and",
1123883957b1SMarco Elver 	"__tsan_atomic32_fetch_and",
1124883957b1SMarco Elver 	"__tsan_atomic64_fetch_and",
1125883957b1SMarco Elver 	"__tsan_atomic8_fetch_or",
1126883957b1SMarco Elver 	"__tsan_atomic16_fetch_or",
1127883957b1SMarco Elver 	"__tsan_atomic32_fetch_or",
1128883957b1SMarco Elver 	"__tsan_atomic64_fetch_or",
1129883957b1SMarco Elver 	"__tsan_atomic8_fetch_xor",
1130883957b1SMarco Elver 	"__tsan_atomic16_fetch_xor",
1131883957b1SMarco Elver 	"__tsan_atomic32_fetch_xor",
1132883957b1SMarco Elver 	"__tsan_atomic64_fetch_xor",
1133883957b1SMarco Elver 	"__tsan_atomic8_fetch_nand",
1134883957b1SMarco Elver 	"__tsan_atomic16_fetch_nand",
1135883957b1SMarco Elver 	"__tsan_atomic32_fetch_nand",
1136883957b1SMarco Elver 	"__tsan_atomic64_fetch_nand",
1137883957b1SMarco Elver 	"__tsan_atomic8_compare_exchange_strong",
1138883957b1SMarco Elver 	"__tsan_atomic16_compare_exchange_strong",
1139883957b1SMarco Elver 	"__tsan_atomic32_compare_exchange_strong",
1140883957b1SMarco Elver 	"__tsan_atomic64_compare_exchange_strong",
1141883957b1SMarco Elver 	"__tsan_atomic8_compare_exchange_weak",
1142883957b1SMarco Elver 	"__tsan_atomic16_compare_exchange_weak",
1143883957b1SMarco Elver 	"__tsan_atomic32_compare_exchange_weak",
1144883957b1SMarco Elver 	"__tsan_atomic64_compare_exchange_weak",
1145883957b1SMarco Elver 	"__tsan_atomic8_compare_exchange_val",
1146883957b1SMarco Elver 	"__tsan_atomic16_compare_exchange_val",
1147883957b1SMarco Elver 	"__tsan_atomic32_compare_exchange_val",
1148883957b1SMarco Elver 	"__tsan_atomic64_compare_exchange_val",
1149883957b1SMarco Elver 	"__tsan_atomic_thread_fence",
1150883957b1SMarco Elver 	"__tsan_atomic_signal_fence",
1151d5d46924SArnd Bergmann 	"__tsan_unaligned_read16",
1152d5d46924SArnd Bergmann 	"__tsan_unaligned_write16",
1153ea24213dSPeter Zijlstra 	/* KCOV */
1154ea24213dSPeter Zijlstra 	"write_comp_data",
1155ae033f08SJosh Poimboeuf 	"check_kcov_mode",
1156ea24213dSPeter Zijlstra 	"__sanitizer_cov_trace_pc",
1157ea24213dSPeter Zijlstra 	"__sanitizer_cov_trace_const_cmp1",
1158ea24213dSPeter Zijlstra 	"__sanitizer_cov_trace_const_cmp2",
1159ea24213dSPeter Zijlstra 	"__sanitizer_cov_trace_const_cmp4",
1160ea24213dSPeter Zijlstra 	"__sanitizer_cov_trace_const_cmp8",
1161ea24213dSPeter Zijlstra 	"__sanitizer_cov_trace_cmp1",
1162ea24213dSPeter Zijlstra 	"__sanitizer_cov_trace_cmp2",
1163ea24213dSPeter Zijlstra 	"__sanitizer_cov_trace_cmp4",
1164ea24213dSPeter Zijlstra 	"__sanitizer_cov_trace_cmp8",
116536b1c700SAl Viro 	"__sanitizer_cov_trace_switch",
116640b22c9dSAlexander Potapenko 	/* KMSAN */
116740b22c9dSAlexander Potapenko 	"kmsan_copy_to_user",
1168ec3e837dSIlya Leoshkevich 	"kmsan_disable_current",
1169ec3e837dSIlya Leoshkevich 	"kmsan_enable_current",
117040b22c9dSAlexander Potapenko 	"kmsan_report",
117140b22c9dSAlexander Potapenko 	"kmsan_unpoison_entry_regs",
117240b22c9dSAlexander Potapenko 	"kmsan_unpoison_memory",
117340b22c9dSAlexander Potapenko 	"__msan_chain_origin",
117440b22c9dSAlexander Potapenko 	"__msan_get_context_state",
117540b22c9dSAlexander Potapenko 	"__msan_instrument_asm_store",
117640b22c9dSAlexander Potapenko 	"__msan_metadata_ptr_for_load_1",
117740b22c9dSAlexander Potapenko 	"__msan_metadata_ptr_for_load_2",
117840b22c9dSAlexander Potapenko 	"__msan_metadata_ptr_for_load_4",
117940b22c9dSAlexander Potapenko 	"__msan_metadata_ptr_for_load_8",
118040b22c9dSAlexander Potapenko 	"__msan_metadata_ptr_for_load_n",
118140b22c9dSAlexander Potapenko 	"__msan_metadata_ptr_for_store_1",
118240b22c9dSAlexander Potapenko 	"__msan_metadata_ptr_for_store_2",
118340b22c9dSAlexander Potapenko 	"__msan_metadata_ptr_for_store_4",
118440b22c9dSAlexander Potapenko 	"__msan_metadata_ptr_for_store_8",
118540b22c9dSAlexander Potapenko 	"__msan_metadata_ptr_for_store_n",
118640b22c9dSAlexander Potapenko 	"__msan_poison_alloca",
118740b22c9dSAlexander Potapenko 	"__msan_warning",
1188ea24213dSPeter Zijlstra 	/* UBSAN */
1189ea24213dSPeter Zijlstra 	"ubsan_type_mismatch_common",
1190ea24213dSPeter Zijlstra 	"__ubsan_handle_type_mismatch",
1191ea24213dSPeter Zijlstra 	"__ubsan_handle_type_mismatch_v1",
11929a50dcafSPeter Zijlstra 	"__ubsan_handle_shift_out_of_bounds",
1193f18b0d7eSPeter Zijlstra 	"__ubsan_handle_load_invalid_value",
11947f530fbaSJosh Poimboeuf 	/* STACKLEAK */
11957f530fbaSJosh Poimboeuf 	"stackleak_track_stack",
119672c774aaSJosh Poimboeuf 	/* TRACE_BRANCH_PROFILING */
119772c774aaSJosh Poimboeuf 	"ftrace_likely_update",
119872c774aaSJosh Poimboeuf 	/* STACKPROTECTOR */
119972c774aaSJosh Poimboeuf 	"__stack_chk_fail",
1200ea24213dSPeter Zijlstra 	/* misc */
1201ea24213dSPeter Zijlstra 	"csum_partial_copy_generic",
1202ec6347bbSDan Williams 	"copy_mc_fragile",
1203ec6347bbSDan Williams 	"copy_mc_fragile_handle_tail",
12045da8e4a6SDan Williams 	"copy_mc_enhanced_fast_string",
12058c9b6a88SLinus Torvalds 	"rep_stos_alternative",
1206427fda2cSLinus Torvalds 	"rep_movs_alternative",
12073639a535SLinus Torvalds 	"__copy_user_nocache",
1208ea24213dSPeter Zijlstra 	NULL
1209ea24213dSPeter Zijlstra };
1210ea24213dSPeter Zijlstra 
add_uaccess_safe(struct objtool_file * file)1211ea24213dSPeter Zijlstra static void add_uaccess_safe(struct objtool_file *file)
1212ea24213dSPeter Zijlstra {
1213ea24213dSPeter Zijlstra 	struct symbol *func;
1214ea24213dSPeter Zijlstra 	const char **name;
1215ea24213dSPeter Zijlstra 
12162daf7fabSJosh Poimboeuf 	if (!opts.uaccess)
1217ea24213dSPeter Zijlstra 		return;
1218ea24213dSPeter Zijlstra 
1219ea24213dSPeter Zijlstra 	for (name = uaccess_safe_builtin; *name; name++) {
1220ea24213dSPeter Zijlstra 		func = find_symbol_by_name(file->elf, *name);
1221ea24213dSPeter Zijlstra 		if (!func)
1222ea24213dSPeter Zijlstra 			continue;
1223ea24213dSPeter Zijlstra 
1224e10cd8feSJosh Poimboeuf 		func->uaccess_safe = true;
1225ea24213dSPeter Zijlstra 	}
1226dcc914f4SJosh Poimboeuf }
1227dcc914f4SJosh Poimboeuf 
1228dcc914f4SJosh Poimboeuf /*
12294ae68b26SPeter Zijlstra  * Symbols that replace INSN_CALL_DYNAMIC, every (tail) call to such a symbol
12304ae68b26SPeter Zijlstra  * will be added to the .retpoline_sites section.
12314ae68b26SPeter Zijlstra  */
arch_is_retpoline(struct symbol * sym)1232530b4dddSPeter Zijlstra __weak bool arch_is_retpoline(struct symbol *sym)
1233530b4dddSPeter Zijlstra {
1234530b4dddSPeter Zijlstra 	return false;
1235530b4dddSPeter Zijlstra }
1236530b4dddSPeter Zijlstra 
12374ae68b26SPeter Zijlstra /*
12384ae68b26SPeter Zijlstra  * Symbols that replace INSN_RETURN, every (tail) call to such a symbol
12394ae68b26SPeter Zijlstra  * will be added to the .return_sites section.
12404ae68b26SPeter Zijlstra  */
arch_is_rethunk(struct symbol * sym)1241d9e9d230SPeter Zijlstra __weak bool arch_is_rethunk(struct symbol *sym)
1242d9e9d230SPeter Zijlstra {
1243d9e9d230SPeter Zijlstra 	return false;
1244d9e9d230SPeter Zijlstra }
1245d9e9d230SPeter Zijlstra 
12464ae68b26SPeter Zijlstra /*
12474ae68b26SPeter Zijlstra  * Symbols that are embedded inside other instructions, because sometimes crazy
12484ae68b26SPeter Zijlstra  * code exists. These are mostly ignored for validation purposes.
12494ae68b26SPeter Zijlstra  */
arch_is_embedded_insn(struct symbol * sym)12504ae68b26SPeter Zijlstra __weak bool arch_is_embedded_insn(struct symbol *sym)
12514ae68b26SPeter Zijlstra {
12524ae68b26SPeter Zijlstra 	return false;
12534ae68b26SPeter Zijlstra }
12544ae68b26SPeter Zijlstra 
insn_reloc(struct objtool_file * file,struct instruction * insn)12557bd2a600SPeter Zijlstra static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn)
12567bd2a600SPeter Zijlstra {
12570932dbe1SPeter Zijlstra 	struct reloc *reloc;
12580932dbe1SPeter Zijlstra 
12590932dbe1SPeter Zijlstra 	if (insn->no_reloc)
12607bd2a600SPeter Zijlstra 		return NULL;
12617bd2a600SPeter Zijlstra 
1262db2b0c5dSPeter Zijlstra 	if (!file)
1263db2b0c5dSPeter Zijlstra 		return NULL;
1264db2b0c5dSPeter Zijlstra 
12650932dbe1SPeter Zijlstra 	reloc = find_reloc_by_dest_range(file->elf, insn->sec,
12667bd2a600SPeter Zijlstra 					 insn->offset, insn->len);
12670932dbe1SPeter Zijlstra 	if (!reloc) {
12680932dbe1SPeter Zijlstra 		insn->no_reloc = 1;
12697bd2a600SPeter Zijlstra 		return NULL;
12707bd2a600SPeter Zijlstra 	}
12717bd2a600SPeter Zijlstra 
12720932dbe1SPeter Zijlstra 	return reloc;
12737bd2a600SPeter Zijlstra }
12747bd2a600SPeter Zijlstra 
remove_insn_ops(struct instruction * insn)1275f56dae88SPeter Zijlstra static void remove_insn_ops(struct instruction *insn)
1276f56dae88SPeter Zijlstra {
12773ee88df1SPeter Zijlstra 	struct stack_op *op, *next;
1278f56dae88SPeter Zijlstra 
12793ee88df1SPeter Zijlstra 	for (op = insn->stack_ops; op; op = next) {
12803ee88df1SPeter Zijlstra 		next = op->next;
1281f56dae88SPeter Zijlstra 		free(op);
1282f56dae88SPeter Zijlstra 	}
12833ee88df1SPeter Zijlstra 	insn->stack_ops = NULL;
1284f56dae88SPeter Zijlstra }
1285f56dae88SPeter Zijlstra 
annotate_call_site(struct objtool_file * file,struct instruction * insn,bool sibling)1286c5995abeSJosh Poimboeuf static int annotate_call_site(struct objtool_file *file,
1287dd003edeSPeter Zijlstra 			       struct instruction *insn, bool sibling)
1288f56dae88SPeter Zijlstra {
1289f56dae88SPeter Zijlstra 	struct reloc *reloc = insn_reloc(file, insn);
1290c6f5dc28SPeter Zijlstra 	struct symbol *sym = insn_call_dest(insn);
1291f56dae88SPeter Zijlstra 
1292dd003edeSPeter Zijlstra 	if (!sym)
1293dd003edeSPeter Zijlstra 		sym = reloc->sym;
1294dd003edeSPeter Zijlstra 
1295dd003edeSPeter Zijlstra 	if (sym->static_call_tramp) {
1296dd003edeSPeter Zijlstra 		list_add_tail(&insn->call_node, &file->static_call_list);
1297c5995abeSJosh Poimboeuf 		return 0;
1298f56dae88SPeter Zijlstra 	}
1299f56dae88SPeter Zijlstra 
1300134ab5bdSPeter Zijlstra 	if (sym->retpoline_thunk) {
1301134ab5bdSPeter Zijlstra 		list_add_tail(&insn->call_node, &file->retpoline_call_list);
1302c5995abeSJosh Poimboeuf 		return 0;
1303134ab5bdSPeter Zijlstra 	}
1304134ab5bdSPeter Zijlstra 
1305f56dae88SPeter Zijlstra 	/*
130605098119SMarco Elver 	 * Many compilers cannot disable KCOV or sanitizer calls with a function
130705098119SMarco Elver 	 * attribute so they need a little help, NOP out any such calls from
130805098119SMarco Elver 	 * noinstr text.
1309f56dae88SPeter Zijlstra 	 */
131022102f45SJosh Poimboeuf 	if (opts.hack_noinstr && insn->sec->noinstr && sym->profiling_func) {
1311ec24b927SJosh Poimboeuf 		if (reloc)
1312ec24b927SJosh Poimboeuf 			set_reloc_type(file->elf, reloc, R_NONE);
1313f56dae88SPeter Zijlstra 
1314c5995abeSJosh Poimboeuf 		if (elf_write_insn(file->elf, insn->sec,
1315f56dae88SPeter Zijlstra 				   insn->offset, insn->len,
1316f56dae88SPeter Zijlstra 				   sibling ? arch_ret_insn(insn->len)
1317c5995abeSJosh Poimboeuf 					   : arch_nop_insn(insn->len))) {
1318c5995abeSJosh Poimboeuf 			return -1;
1319c5995abeSJosh Poimboeuf 		}
1320f56dae88SPeter Zijlstra 
1321f56dae88SPeter Zijlstra 		insn->type = sibling ? INSN_RETURN : INSN_NOP;
13227a53f408SPeter Zijlstra 
13237a53f408SPeter Zijlstra 		if (sibling) {
13247a53f408SPeter Zijlstra 			/*
13257a53f408SPeter Zijlstra 			 * We've replaced the tail-call JMP insn by two new
13267a53f408SPeter Zijlstra 			 * insn: RET; INT3, except we only have a single struct
13277a53f408SPeter Zijlstra 			 * insn here. Mark it retpoline_safe to avoid the SLS
13287a53f408SPeter Zijlstra 			 * warning, instead of adding another insn.
13297a53f408SPeter Zijlstra 			 */
13307a53f408SPeter Zijlstra 			insn->retpoline_safe = true;
13317a53f408SPeter Zijlstra 		}
13327a53f408SPeter Zijlstra 
1333c5995abeSJosh Poimboeuf 		return 0;
1334f56dae88SPeter Zijlstra 	}
1335f56dae88SPeter Zijlstra 
13362daf7fabSJosh Poimboeuf 	if (opts.mcount && sym->fentry) {
1337f56dae88SPeter Zijlstra 		if (sibling)
1338246b2c85SJosh Poimboeuf 			WARN_INSN(insn, "tail call to __fentry__ !?!?");
1339280981d6SSathvika Vasireddy 		if (opts.mnop) {
1340ec24b927SJosh Poimboeuf 			if (reloc)
1341ec24b927SJosh Poimboeuf 				set_reloc_type(file->elf, reloc, R_NONE);
1342f56dae88SPeter Zijlstra 
1343c5995abeSJosh Poimboeuf 			if (elf_write_insn(file->elf, insn->sec,
1344f56dae88SPeter Zijlstra 					   insn->offset, insn->len,
1345c5995abeSJosh Poimboeuf 					   arch_nop_insn(insn->len))) {
1346c5995abeSJosh Poimboeuf 				return -1;
1347c5995abeSJosh Poimboeuf 			}
1348f56dae88SPeter Zijlstra 
1349f56dae88SPeter Zijlstra 			insn->type = INSN_NOP;
1350280981d6SSathvika Vasireddy 		}
1351f56dae88SPeter Zijlstra 
1352c509331bSPeter Zijlstra 		list_add_tail(&insn->call_node, &file->mcount_loc_list);
1353c5995abeSJosh Poimboeuf 		return 0;
1354f56dae88SPeter Zijlstra 	}
13550e5b613bSPeter Zijlstra 
1356ab9fea59SPeter Zijlstra 	if (insn->type == INSN_CALL && !insn->sec->init &&
1357ab9fea59SPeter Zijlstra 	    !insn->_call_dest->embedded_insn)
135800abd384SPeter Zijlstra 		list_add_tail(&insn->call_node, &file->call_list);
135900abd384SPeter Zijlstra 
13600e5b613bSPeter Zijlstra 	if (!sibling && dead_end_function(file, sym))
13610e5b613bSPeter Zijlstra 		insn->dead_end = true;
1362c5995abeSJosh Poimboeuf 
1363c5995abeSJosh Poimboeuf 	return 0;
1364dd003edeSPeter Zijlstra }
1365dd003edeSPeter Zijlstra 
add_call_dest(struct objtool_file * file,struct instruction * insn,struct symbol * dest,bool sibling)1366c5995abeSJosh Poimboeuf static int add_call_dest(struct objtool_file *file, struct instruction *insn,
1367dd003edeSPeter Zijlstra 			  struct symbol *dest, bool sibling)
1368dd003edeSPeter Zijlstra {
1369c6f5dc28SPeter Zijlstra 	insn->_call_dest = dest;
1370dd003edeSPeter Zijlstra 	if (!dest)
1371c5995abeSJosh Poimboeuf 		return 0;
1372f56dae88SPeter Zijlstra 
1373f56dae88SPeter Zijlstra 	/*
1374f56dae88SPeter Zijlstra 	 * Whatever stack impact regular CALLs have, should be undone
1375f56dae88SPeter Zijlstra 	 * by the RETURN of the called function.
1376f56dae88SPeter Zijlstra 	 *
1377f56dae88SPeter Zijlstra 	 * Annotated intra-function calls retain the stack_ops but
1378f56dae88SPeter Zijlstra 	 * are converted to JUMP, see read_intra_function_calls().
1379f56dae88SPeter Zijlstra 	 */
1380f56dae88SPeter Zijlstra 	remove_insn_ops(insn);
1381dd003edeSPeter Zijlstra 
1382c5995abeSJosh Poimboeuf 	return annotate_call_site(file, insn, sibling);
1383f56dae88SPeter Zijlstra }
1384f56dae88SPeter Zijlstra 
add_retpoline_call(struct objtool_file * file,struct instruction * insn)1385c5995abeSJosh Poimboeuf static int add_retpoline_call(struct objtool_file *file, struct instruction *insn)
1386134ab5bdSPeter Zijlstra {
1387134ab5bdSPeter Zijlstra 	/*
1388134ab5bdSPeter Zijlstra 	 * Retpoline calls/jumps are really dynamic calls/jumps in disguise,
1389134ab5bdSPeter Zijlstra 	 * so convert them accordingly.
1390134ab5bdSPeter Zijlstra 	 */
1391134ab5bdSPeter Zijlstra 	switch (insn->type) {
1392134ab5bdSPeter Zijlstra 	case INSN_CALL:
1393134ab5bdSPeter Zijlstra 		insn->type = INSN_CALL_DYNAMIC;
1394134ab5bdSPeter Zijlstra 		break;
1395134ab5bdSPeter Zijlstra 	case INSN_JUMP_UNCONDITIONAL:
1396134ab5bdSPeter Zijlstra 		insn->type = INSN_JUMP_DYNAMIC;
1397134ab5bdSPeter Zijlstra 		break;
1398134ab5bdSPeter Zijlstra 	case INSN_JUMP_CONDITIONAL:
1399134ab5bdSPeter Zijlstra 		insn->type = INSN_JUMP_DYNAMIC_CONDITIONAL;
1400134ab5bdSPeter Zijlstra 		break;
1401134ab5bdSPeter Zijlstra 	default:
1402c5995abeSJosh Poimboeuf 		return 0;
1403134ab5bdSPeter Zijlstra 	}
1404134ab5bdSPeter Zijlstra 
1405134ab5bdSPeter Zijlstra 	insn->retpoline_safe = true;
1406134ab5bdSPeter Zijlstra 
1407134ab5bdSPeter Zijlstra 	/*
1408134ab5bdSPeter Zijlstra 	 * Whatever stack impact regular CALLs have, should be undone
1409134ab5bdSPeter Zijlstra 	 * by the RETURN of the called function.
1410134ab5bdSPeter Zijlstra 	 *
1411134ab5bdSPeter Zijlstra 	 * Annotated intra-function calls retain the stack_ops but
1412134ab5bdSPeter Zijlstra 	 * are converted to JUMP, see read_intra_function_calls().
1413134ab5bdSPeter Zijlstra 	 */
1414134ab5bdSPeter Zijlstra 	remove_insn_ops(insn);
1415134ab5bdSPeter Zijlstra 
1416c5995abeSJosh Poimboeuf 	return annotate_call_site(file, insn, false);
1417134ab5bdSPeter Zijlstra }
141808f87a93SPeter Zijlstra 
add_return_call(struct objtool_file * file,struct instruction * insn,bool add)1419a149180fSPeter Zijlstra static void add_return_call(struct objtool_file *file, struct instruction *insn, bool add)
1420d9e9d230SPeter Zijlstra {
1421d9e9d230SPeter Zijlstra 	/*
1422d9e9d230SPeter Zijlstra 	 * Return thunk tail calls are really just returns in disguise,
1423d9e9d230SPeter Zijlstra 	 * so convert them accordingly.
1424d9e9d230SPeter Zijlstra 	 */
1425d9e9d230SPeter Zijlstra 	insn->type = INSN_RETURN;
1426d9e9d230SPeter Zijlstra 	insn->retpoline_safe = true;
1427d9e9d230SPeter Zijlstra 
1428a149180fSPeter Zijlstra 	if (add)
1429d9e9d230SPeter Zijlstra 		list_add_tail(&insn->call_node, &file->return_thunk_list);
1430d9e9d230SPeter Zijlstra }
1431d9e9d230SPeter Zijlstra 
is_first_func_insn(struct objtool_file * file,struct instruction * insn,struct symbol * sym)14325a9c361aSPeter Zijlstra static bool is_first_func_insn(struct objtool_file *file,
14335a9c361aSPeter Zijlstra 			       struct instruction *insn, struct symbol *sym)
143408f87a93SPeter Zijlstra {
14355a9c361aSPeter Zijlstra 	if (insn->offset == sym->offset)
1436d139bca4SPeter Zijlstra 		return true;
1437d139bca4SPeter Zijlstra 
14385a9c361aSPeter Zijlstra 	/* Allow direct CALL/JMP past ENDBR */
14392daf7fabSJosh Poimboeuf 	if (opts.ibt) {
1440d139bca4SPeter Zijlstra 		struct instruction *prev = prev_insn_same_sym(file, insn);
1441d139bca4SPeter Zijlstra 
1442d139bca4SPeter Zijlstra 		if (prev && prev->type == INSN_ENDBR &&
14435a9c361aSPeter Zijlstra 		    insn->offset == sym->offset + prev->len)
1444d139bca4SPeter Zijlstra 			return true;
1445d139bca4SPeter Zijlstra 	}
1446d139bca4SPeter Zijlstra 
1447d139bca4SPeter Zijlstra 	return false;
144808f87a93SPeter Zijlstra }
144908f87a93SPeter Zijlstra 
1450258c7605SJosh Poimboeuf /*
14515a9c361aSPeter Zijlstra  * A sibling call is a tail-call to another symbol -- to differentiate from a
14525a9c361aSPeter Zijlstra  * recursive tail-call which is to the same symbol.
14535a9c361aSPeter Zijlstra  */
jump_is_sibling_call(struct objtool_file * file,struct instruction * from,struct instruction * to)14545a9c361aSPeter Zijlstra static bool jump_is_sibling_call(struct objtool_file *file,
14555a9c361aSPeter Zijlstra 				 struct instruction *from, struct instruction *to)
14565a9c361aSPeter Zijlstra {
14575a9c361aSPeter Zijlstra 	struct symbol *fs = from->sym;
14585a9c361aSPeter Zijlstra 	struct symbol *ts = to->sym;
14595a9c361aSPeter Zijlstra 
14605a9c361aSPeter Zijlstra 	/* Not a sibling call if from/to a symbol hole */
14615a9c361aSPeter Zijlstra 	if (!fs || !ts)
14625a9c361aSPeter Zijlstra 		return false;
14635a9c361aSPeter Zijlstra 
14645a9c361aSPeter Zijlstra 	/* Not a sibling call if not targeting the start of a symbol. */
14655a9c361aSPeter Zijlstra 	if (!is_first_func_insn(file, to, ts))
14665a9c361aSPeter Zijlstra 		return false;
14675a9c361aSPeter Zijlstra 
14685a9c361aSPeter Zijlstra 	/* Disallow sibling calls into STT_NOTYPE */
14695a9c361aSPeter Zijlstra 	if (ts->type == STT_NOTYPE)
14705a9c361aSPeter Zijlstra 		return false;
14715a9c361aSPeter Zijlstra 
14725a9c361aSPeter Zijlstra 	/* Must not be self to be a sibling */
14735a9c361aSPeter Zijlstra 	return fs->pfunc != ts->pfunc;
14745a9c361aSPeter Zijlstra }
14755a9c361aSPeter Zijlstra 
14765a9c361aSPeter Zijlstra /*
1477dcc914f4SJosh Poimboeuf  * Find the destination instructions for all jumps.
1478dcc914f4SJosh Poimboeuf  */
add_jump_destinations(struct objtool_file * file)1479dcc914f4SJosh Poimboeuf static int add_jump_destinations(struct objtool_file *file)
1480dcc914f4SJosh Poimboeuf {
148126ff6041SJosh Poimboeuf 	struct instruction *insn, *jump_dest;
1482f1974222SMatt Helsley 	struct reloc *reloc;
1483dcc914f4SJosh Poimboeuf 	struct section *dest_sec;
1484dcc914f4SJosh Poimboeuf 	unsigned long dest_off;
1485c5995abeSJosh Poimboeuf 	int ret;
1486dcc914f4SJosh Poimboeuf 
1487dcc914f4SJosh Poimboeuf 	for_each_insn(file, insn) {
14880d759774SJosh Poimboeuf 		struct symbol *func = insn_func(insn);
14890d759774SJosh Poimboeuf 
149034c861e8SJosh Poimboeuf 		if (insn->jump_dest) {
149134c861e8SJosh Poimboeuf 			/*
149234c861e8SJosh Poimboeuf 			 * handle_group_alt() may have previously set
149334c861e8SJosh Poimboeuf 			 * 'jump_dest' for some alternatives.
149434c861e8SJosh Poimboeuf 			 */
149534c861e8SJosh Poimboeuf 			continue;
149634c861e8SJosh Poimboeuf 		}
1497a2296140SJosh Poimboeuf 		if (!is_static_jump(insn))
1498dcc914f4SJosh Poimboeuf 			continue;
1499dcc914f4SJosh Poimboeuf 
15007bd2a600SPeter Zijlstra 		reloc = insn_reloc(file, insn);
1501f1974222SMatt Helsley 		if (!reloc) {
1502dcc914f4SJosh Poimboeuf 			dest_sec = insn->sec;
1503bfb08f22SRaphael Gault 			dest_off = arch_jump_destination(insn);
1504f1974222SMatt Helsley 		} else if (reloc->sym->type == STT_SECTION) {
1505f1974222SMatt Helsley 			dest_sec = reloc->sym->sec;
15060696b6e3SJosh Poimboeuf 			dest_off = arch_dest_reloc_offset(reloc_addend(reloc));
15071739c66eSPeter Zijlstra 		} else if (reloc->sym->retpoline_thunk) {
1508c5995abeSJosh Poimboeuf 			ret = add_retpoline_call(file, insn);
1509c5995abeSJosh Poimboeuf 			if (ret)
1510c5995abeSJosh Poimboeuf 				return ret;
151139b73533SJosh Poimboeuf 			continue;
1512d9e9d230SPeter Zijlstra 		} else if (reloc->sym->return_thunk) {
1513a149180fSPeter Zijlstra 			add_return_call(file, insn, true);
1514d9e9d230SPeter Zijlstra 			continue;
15150d759774SJosh Poimboeuf 		} else if (func) {
151626ff6041SJosh Poimboeuf 			/*
151726ff6041SJosh Poimboeuf 			 * External sibling call or internal sibling call with
151826ff6041SJosh Poimboeuf 			 * STT_FUNC reloc.
151926ff6041SJosh Poimboeuf 			 */
1520c5995abeSJosh Poimboeuf 			ret = add_call_dest(file, insn, reloc->sym, true);
1521c5995abeSJosh Poimboeuf 			if (ret)
1522c5995abeSJosh Poimboeuf 				return ret;
1523dcc914f4SJosh Poimboeuf 			continue;
1524ecf11ba4SJosh Poimboeuf 		} else if (reloc->sym->sec->idx) {
1525ecf11ba4SJosh Poimboeuf 			dest_sec = reloc->sym->sec;
1526ecf11ba4SJosh Poimboeuf 			dest_off = reloc->sym->sym.st_value +
15270696b6e3SJosh Poimboeuf 				   arch_dest_reloc_offset(reloc_addend(reloc));
1528ecf11ba4SJosh Poimboeuf 		} else {
1529ecf11ba4SJosh Poimboeuf 			/* non-func asm code jumping to another file */
1530ecf11ba4SJosh Poimboeuf 			continue;
1531dcc914f4SJosh Poimboeuf 		}
1532dcc914f4SJosh Poimboeuf 
153326ff6041SJosh Poimboeuf 		jump_dest = find_insn(file, dest_sec, dest_off);
153426ff6041SJosh Poimboeuf 		if (!jump_dest) {
1535a149180fSPeter Zijlstra 			struct symbol *sym = find_symbol_by_offset(dest_sec, dest_off);
1536a149180fSPeter Zijlstra 
1537a149180fSPeter Zijlstra 			/*
1538d025b7baSPeter Zijlstra 			 * This is a special case for retbleed_untrain_ret().
1539a149180fSPeter Zijlstra 			 * It jumps to __x86_return_thunk(), but objtool
1540a149180fSPeter Zijlstra 			 * can't find the thunk's starting RET
1541a149180fSPeter Zijlstra 			 * instruction, because the RET is also in the
1542a149180fSPeter Zijlstra 			 * middle of another instruction.  Objtool only
1543a149180fSPeter Zijlstra 			 * knows about the outer instruction.
1544a149180fSPeter Zijlstra 			 */
15454ae68b26SPeter Zijlstra 			if (sym && sym->embedded_insn) {
1546a149180fSPeter Zijlstra 				add_return_call(file, insn, false);
1547a149180fSPeter Zijlstra 				continue;
1548a149180fSPeter Zijlstra 			}
1549a149180fSPeter Zijlstra 
15500d759774SJosh Poimboeuf 			/*
15510d759774SJosh Poimboeuf 			 * GCOV/KCOV dead code can jump to the end of the
15520d759774SJosh Poimboeuf 			 * function/section.
15530d759774SJosh Poimboeuf 			 */
15540d759774SJosh Poimboeuf 			if (file->ignore_unreachables && func &&
15550d759774SJosh Poimboeuf 			    dest_sec == insn->sec &&
15560d759774SJosh Poimboeuf 			    dest_off == func->offset + func->len)
15570d759774SJosh Poimboeuf 				continue;
15580d759774SJosh Poimboeuf 
15593e7be635SJosh Poimboeuf 			ERROR_INSN(insn, "can't find jump dest instruction at %s+0x%lx",
1560246b2c85SJosh Poimboeuf 				   dest_sec->name, dest_off);
1561dcc914f4SJosh Poimboeuf 			return -1;
1562dcc914f4SJosh Poimboeuf 		}
1563cd77849aSJosh Poimboeuf 
1564cd77849aSJosh Poimboeuf 		/*
156534de4fe7SJosh Poimboeuf 		 * An intra-TU jump in retpoline.o might not have a relocation
156634de4fe7SJosh Poimboeuf 		 * for its jump dest, in which case the above
156734de4fe7SJosh Poimboeuf 		 * add_{retpoline,return}_call() didn't happen.
156834de4fe7SJosh Poimboeuf 		 */
156934de4fe7SJosh Poimboeuf 		if (jump_dest->sym && jump_dest->offset == jump_dest->sym->offset) {
157034de4fe7SJosh Poimboeuf 			if (jump_dest->sym->retpoline_thunk) {
1571c5995abeSJosh Poimboeuf 				ret = add_retpoline_call(file, insn);
1572c5995abeSJosh Poimboeuf 				if (ret)
1573c5995abeSJosh Poimboeuf 					return ret;
157434de4fe7SJosh Poimboeuf 				continue;
157534de4fe7SJosh Poimboeuf 			}
157634de4fe7SJosh Poimboeuf 			if (jump_dest->sym->return_thunk) {
157734de4fe7SJosh Poimboeuf 				add_return_call(file, insn, true);
157834de4fe7SJosh Poimboeuf 				continue;
157934de4fe7SJosh Poimboeuf 			}
158034de4fe7SJosh Poimboeuf 		}
158134de4fe7SJosh Poimboeuf 
158234de4fe7SJosh Poimboeuf 		/*
158354262aa2SPeter Zijlstra 		 * Cross-function jump.
1584cd77849aSJosh Poimboeuf 		 */
15850d759774SJosh Poimboeuf 		if (func && insn_func(jump_dest) && func != insn_func(jump_dest)) {
158654262aa2SPeter Zijlstra 
158754262aa2SPeter Zijlstra 			/*
158854262aa2SPeter Zijlstra 			 * For GCC 8+, create parent/child links for any cold
158954262aa2SPeter Zijlstra 			 * subfunctions.  This is _mostly_ redundant with a
159054262aa2SPeter Zijlstra 			 * similar initialization in read_symbols().
159154262aa2SPeter Zijlstra 			 *
159254262aa2SPeter Zijlstra 			 * If a function has aliases, we want the *first* such
159354262aa2SPeter Zijlstra 			 * function in the symbol table to be the subfunction's
159454262aa2SPeter Zijlstra 			 * parent.  In that case we overwrite the
159554262aa2SPeter Zijlstra 			 * initialization done in read_symbols().
159654262aa2SPeter Zijlstra 			 *
159754262aa2SPeter Zijlstra 			 * However this code can't completely replace the
159854262aa2SPeter Zijlstra 			 * read_symbols() code because this doesn't detect the
159954262aa2SPeter Zijlstra 			 * case where the parent function's only reference to a
1600e7c2bc37SJosh Poimboeuf 			 * subfunction is through a jump table.
160154262aa2SPeter Zijlstra 			 */
16020d759774SJosh Poimboeuf 			if (!strstr(func->name, ".cold") &&
1603dbcdbdfdSPeter Zijlstra 			    strstr(insn_func(jump_dest)->name, ".cold")) {
16040d759774SJosh Poimboeuf 				func->cfunc = insn_func(jump_dest);
16050d759774SJosh Poimboeuf 				insn_func(jump_dest)->pfunc = func;
16065a9c361aSPeter Zijlstra 			}
16075a9c361aSPeter Zijlstra 		}
160854262aa2SPeter Zijlstra 
16095a9c361aSPeter Zijlstra 		if (jump_is_sibling_call(file, insn, jump_dest)) {
161026ff6041SJosh Poimboeuf 			/*
161126ff6041SJosh Poimboeuf 			 * Internal sibling call without reloc or with
161226ff6041SJosh Poimboeuf 			 * STT_SECTION reloc.
161326ff6041SJosh Poimboeuf 			 */
1614c5995abeSJosh Poimboeuf 			ret = add_call_dest(file, insn, insn_func(jump_dest), true);
1615c5995abeSJosh Poimboeuf 			if (ret)
1616c5995abeSJosh Poimboeuf 				return ret;
161726ff6041SJosh Poimboeuf 			continue;
161854262aa2SPeter Zijlstra 		}
161926ff6041SJosh Poimboeuf 
162026ff6041SJosh Poimboeuf 		insn->jump_dest = jump_dest;
1621dcc914f4SJosh Poimboeuf 	}
1622dcc914f4SJosh Poimboeuf 
1623dcc914f4SJosh Poimboeuf 	return 0;
1624dcc914f4SJosh Poimboeuf }
1625dcc914f4SJosh Poimboeuf 
find_call_destination(struct section * sec,unsigned long offset)16262b232a22SJulien Thierry static struct symbol *find_call_destination(struct section *sec, unsigned long offset)
16272b232a22SJulien Thierry {
16282b232a22SJulien Thierry 	struct symbol *call_dest;
16292b232a22SJulien Thierry 
16302b232a22SJulien Thierry 	call_dest = find_func_by_offset(sec, offset);
16312b232a22SJulien Thierry 	if (!call_dest)
16322b232a22SJulien Thierry 		call_dest = find_symbol_by_offset(sec, offset);
16332b232a22SJulien Thierry 
16342b232a22SJulien Thierry 	return call_dest;
16352b232a22SJulien Thierry }
16362b232a22SJulien Thierry 
1637dcc914f4SJosh Poimboeuf /*
1638dcc914f4SJosh Poimboeuf  * Find the destination instructions for all calls.
1639dcc914f4SJosh Poimboeuf  */
add_call_destinations(struct objtool_file * file)1640dcc914f4SJosh Poimboeuf static int add_call_destinations(struct objtool_file *file)
1641dcc914f4SJosh Poimboeuf {
1642dcc914f4SJosh Poimboeuf 	struct instruction *insn;
1643dcc914f4SJosh Poimboeuf 	unsigned long dest_off;
1644f56dae88SPeter Zijlstra 	struct symbol *dest;
1645f1974222SMatt Helsley 	struct reloc *reloc;
1646c5995abeSJosh Poimboeuf 	int ret;
1647dcc914f4SJosh Poimboeuf 
1648dcc914f4SJosh Poimboeuf 	for_each_insn(file, insn) {
1649c84301d7SJosh Poimboeuf 		struct symbol *func = insn_func(insn);
1650dcc914f4SJosh Poimboeuf 		if (insn->type != INSN_CALL)
1651dcc914f4SJosh Poimboeuf 			continue;
1652dcc914f4SJosh Poimboeuf 
16537bd2a600SPeter Zijlstra 		reloc = insn_reloc(file, insn);
1654f1974222SMatt Helsley 		if (!reloc) {
1655bfb08f22SRaphael Gault 			dest_off = arch_jump_destination(insn);
1656f56dae88SPeter Zijlstra 			dest = find_call_destination(insn->sec, dest_off);
1657f56dae88SPeter Zijlstra 
1658c5995abeSJosh Poimboeuf 			ret = add_call_dest(file, insn, dest, false);
1659c5995abeSJosh Poimboeuf 			if (ret)
1660c5995abeSJosh Poimboeuf 				return ret;
1661a845c7cfSJosh Poimboeuf 
1662c84301d7SJosh Poimboeuf 			if (func && func->ignore)
16637acfe531SJosh Poimboeuf 				continue;
16647acfe531SJosh Poimboeuf 
1665c6f5dc28SPeter Zijlstra 			if (!insn_call_dest(insn)) {
16663e7be635SJosh Poimboeuf 				ERROR_INSN(insn, "unannotated intra-function call");
1667dcc914f4SJosh Poimboeuf 				return -1;
1668dcc914f4SJosh Poimboeuf 			}
1669a845c7cfSJosh Poimboeuf 
1670c84301d7SJosh Poimboeuf 			if (func && insn_call_dest(insn)->type != STT_FUNC) {
16713e7be635SJosh Poimboeuf 				ERROR_INSN(insn, "unsupported call to non-function");
16727acfe531SJosh Poimboeuf 				return -1;
16737acfe531SJosh Poimboeuf 			}
16747acfe531SJosh Poimboeuf 
1675f1974222SMatt Helsley 		} else if (reloc->sym->type == STT_SECTION) {
16760696b6e3SJosh Poimboeuf 			dest_off = arch_dest_reloc_offset(reloc_addend(reloc));
1677f56dae88SPeter Zijlstra 			dest = find_call_destination(reloc->sym->sec, dest_off);
1678f56dae88SPeter Zijlstra 			if (!dest) {
16793e7be635SJosh Poimboeuf 				ERROR_INSN(insn, "can't find call dest symbol at %s+0x%lx",
1680246b2c85SJosh Poimboeuf 					   reloc->sym->sec->name, dest_off);
1681dcc914f4SJosh Poimboeuf 				return -1;
1682dcc914f4SJosh Poimboeuf 			}
1683bcb1b6ffSPeter Zijlstra 
1684c5995abeSJosh Poimboeuf 			ret = add_call_dest(file, insn, dest, false);
1685c5995abeSJosh Poimboeuf 			if (ret)
1686c5995abeSJosh Poimboeuf 				return ret;
1687f56dae88SPeter Zijlstra 
16881739c66eSPeter Zijlstra 		} else if (reloc->sym->retpoline_thunk) {
1689c5995abeSJosh Poimboeuf 			ret = add_retpoline_call(file, insn);
1690c5995abeSJosh Poimboeuf 			if (ret)
1691c5995abeSJosh Poimboeuf 				return ret;
1692bcb1b6ffSPeter Zijlstra 
1693c5995abeSJosh Poimboeuf 		} else {
1694c5995abeSJosh Poimboeuf 			ret = add_call_dest(file, insn, reloc->sym, false);
1695c5995abeSJosh Poimboeuf 			if (ret)
1696c5995abeSJosh Poimboeuf 				return ret;
1697c5995abeSJosh Poimboeuf 		}
1698dcc914f4SJosh Poimboeuf 	}
1699dcc914f4SJosh Poimboeuf 
1700dcc914f4SJosh Poimboeuf 	return 0;
1701dcc914f4SJosh Poimboeuf }
1702dcc914f4SJosh Poimboeuf 
1703dcc914f4SJosh Poimboeuf /*
1704c9c324dcSJosh Poimboeuf  * The .alternatives section requires some extra special care over and above
1705c9c324dcSJosh Poimboeuf  * other special sections because alternatives are patched in place.
1706dcc914f4SJosh Poimboeuf  */
handle_group_alt(struct objtool_file * file,struct special_alt * special_alt,struct instruction * orig_insn,struct instruction ** new_insn)1707dcc914f4SJosh Poimboeuf static int handle_group_alt(struct objtool_file *file,
1708dcc914f4SJosh Poimboeuf 			    struct special_alt *special_alt,
1709dcc914f4SJosh Poimboeuf 			    struct instruction *orig_insn,
1710dcc914f4SJosh Poimboeuf 			    struct instruction **new_insn)
1711dcc914f4SJosh Poimboeuf {
1712a706bb08SPeter Zijlstra 	struct instruction *last_new_insn = NULL, *insn, *nop = NULL;
1713b23cc71cSJosh Poimboeuf 	struct alt_group *orig_alt_group, *new_alt_group;
1714dcc914f4SJosh Poimboeuf 	unsigned long dest_off;
1715dcc914f4SJosh Poimboeuf 
1716a706bb08SPeter Zijlstra 	orig_alt_group = orig_insn->alt_group;
1717a706bb08SPeter Zijlstra 	if (!orig_alt_group) {
1718a706bb08SPeter Zijlstra 		struct instruction *last_orig_insn = NULL;
1719b23cc71cSJosh Poimboeuf 
1720c5995abeSJosh Poimboeuf 		orig_alt_group = calloc(1, sizeof(*orig_alt_group));
1721b23cc71cSJosh Poimboeuf 		if (!orig_alt_group) {
17223e7be635SJosh Poimboeuf 			ERROR_GLIBC("calloc");
1723b23cc71cSJosh Poimboeuf 			return -1;
1724b23cc71cSJosh Poimboeuf 		}
1725c9c324dcSJosh Poimboeuf 		orig_alt_group->cfi = calloc(special_alt->orig_len,
1726c9c324dcSJosh Poimboeuf 					     sizeof(struct cfi_state *));
1727c9c324dcSJosh Poimboeuf 		if (!orig_alt_group->cfi) {
17283e7be635SJosh Poimboeuf 			ERROR_GLIBC("calloc");
1729c9c324dcSJosh Poimboeuf 			return -1;
1730c9c324dcSJosh Poimboeuf 		}
1731c9c324dcSJosh Poimboeuf 
1732dcc914f4SJosh Poimboeuf 		insn = orig_insn;
1733dcc914f4SJosh Poimboeuf 		sec_for_each_insn_from(file, insn) {
1734dcc914f4SJosh Poimboeuf 			if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
1735dcc914f4SJosh Poimboeuf 				break;
1736dcc914f4SJosh Poimboeuf 
1737b23cc71cSJosh Poimboeuf 			insn->alt_group = orig_alt_group;
1738dcc914f4SJosh Poimboeuf 			last_orig_insn = insn;
1739dcc914f4SJosh Poimboeuf 		}
1740b23cc71cSJosh Poimboeuf 		orig_alt_group->orig_group = NULL;
1741b23cc71cSJosh Poimboeuf 		orig_alt_group->first_insn = orig_insn;
1742b23cc71cSJosh Poimboeuf 		orig_alt_group->last_insn = last_orig_insn;
17431c34496eSPeter Zijlstra 		orig_alt_group->nop = NULL;
17441154bbd3SJosh Poimboeuf 		orig_alt_group->ignore = orig_insn->ignore_alts;
1745a706bb08SPeter Zijlstra 	} else {
1746a706bb08SPeter Zijlstra 		if (orig_alt_group->last_insn->offset + orig_alt_group->last_insn->len -
1747a706bb08SPeter Zijlstra 		    orig_alt_group->first_insn->offset != special_alt->orig_len) {
17483e7be635SJosh Poimboeuf 			ERROR_INSN(orig_insn, "weirdly overlapping alternative! %ld != %d",
1749a706bb08SPeter Zijlstra 				   orig_alt_group->last_insn->offset +
1750a706bb08SPeter Zijlstra 				   orig_alt_group->last_insn->len -
1751a706bb08SPeter Zijlstra 				   orig_alt_group->first_insn->offset,
1752a706bb08SPeter Zijlstra 				   special_alt->orig_len);
1753a706bb08SPeter Zijlstra 			return -1;
1754a706bb08SPeter Zijlstra 		}
1755a706bb08SPeter Zijlstra 	}
1756dcc914f4SJosh Poimboeuf 
1757c5995abeSJosh Poimboeuf 	new_alt_group = calloc(1, sizeof(*new_alt_group));
1758b23cc71cSJosh Poimboeuf 	if (!new_alt_group) {
17593e7be635SJosh Poimboeuf 		ERROR_GLIBC("calloc");
1760dcc914f4SJosh Poimboeuf 		return -1;
1761dcc914f4SJosh Poimboeuf 	}
1762dcc914f4SJosh Poimboeuf 
1763c9c324dcSJosh Poimboeuf 	if (special_alt->new_len < special_alt->orig_len) {
1764c9c324dcSJosh Poimboeuf 		/*
1765c9c324dcSJosh Poimboeuf 		 * Insert a fake nop at the end to make the replacement
1766c9c324dcSJosh Poimboeuf 		 * alt_group the same size as the original.  This is needed to
1767c9c324dcSJosh Poimboeuf 		 * allow propagate_alt_cfi() to do its magic.  When the last
1768c9c324dcSJosh Poimboeuf 		 * instruction affects the stack, the instruction after it (the
1769c9c324dcSJosh Poimboeuf 		 * nop) will propagate the new state to the shared CFI array.
1770c9c324dcSJosh Poimboeuf 		 */
1771c5995abeSJosh Poimboeuf 		nop = calloc(1, sizeof(*nop));
1772c9c324dcSJosh Poimboeuf 		if (!nop) {
17733e7be635SJosh Poimboeuf 			ERROR_GLIBC("calloc");
1774c9c324dcSJosh Poimboeuf 			return -1;
1775c9c324dcSJosh Poimboeuf 		}
1776c9c324dcSJosh Poimboeuf 		memset(nop, 0, sizeof(*nop));
1777c9c324dcSJosh Poimboeuf 
1778c9c324dcSJosh Poimboeuf 		nop->sec = special_alt->new_sec;
1779c9c324dcSJosh Poimboeuf 		nop->offset = special_alt->new_off + special_alt->new_len;
1780c9c324dcSJosh Poimboeuf 		nop->len = special_alt->orig_len - special_alt->new_len;
1781c9c324dcSJosh Poimboeuf 		nop->type = INSN_NOP;
1782dbcdbdfdSPeter Zijlstra 		nop->sym = orig_insn->sym;
1783c9c324dcSJosh Poimboeuf 		nop->alt_group = new_alt_group;
1784dcc914f4SJosh Poimboeuf 	}
1785dcc914f4SJosh Poimboeuf 
1786dcc914f4SJosh Poimboeuf 	if (!special_alt->new_len) {
1787c9c324dcSJosh Poimboeuf 		*new_insn = nop;
1788c9c324dcSJosh Poimboeuf 		goto end;
1789dcc914f4SJosh Poimboeuf 	}
1790dcc914f4SJosh Poimboeuf 
1791dcc914f4SJosh Poimboeuf 	insn = *new_insn;
1792dcc914f4SJosh Poimboeuf 	sec_for_each_insn_from(file, insn) {
179345245f51SJulien Thierry 		struct reloc *alt_reloc;
179445245f51SJulien Thierry 
1795dcc914f4SJosh Poimboeuf 		if (insn->offset >= special_alt->new_off + special_alt->new_len)
1796dcc914f4SJosh Poimboeuf 			break;
1797dcc914f4SJosh Poimboeuf 
1798dcc914f4SJosh Poimboeuf 		last_new_insn = insn;
1799dcc914f4SJosh Poimboeuf 
1800dbcdbdfdSPeter Zijlstra 		insn->sym = orig_insn->sym;
1801b23cc71cSJosh Poimboeuf 		insn->alt_group = new_alt_group;
1802a845c7cfSJosh Poimboeuf 
1803dc419723SJosh Poimboeuf 		/*
1804dc419723SJosh Poimboeuf 		 * Since alternative replacement code is copy/pasted by the
1805dc419723SJosh Poimboeuf 		 * kernel after applying relocations, generally such code can't
1806dc419723SJosh Poimboeuf 		 * have relative-address relocation references to outside the
1807dc419723SJosh Poimboeuf 		 * .altinstr_replacement section, unless the arch's
1808dc419723SJosh Poimboeuf 		 * alternatives code can adjust the relative offsets
1809dc419723SJosh Poimboeuf 		 * accordingly.
1810dc419723SJosh Poimboeuf 		 */
18117bd2a600SPeter Zijlstra 		alt_reloc = insn_reloc(file, insn);
181261c6065eSPeter Zijlstra 		if (alt_reloc && arch_pc_relative_reloc(alt_reloc) &&
181345245f51SJulien Thierry 		    !arch_support_alt_relocation(special_alt, insn, alt_reloc)) {
1814dc419723SJosh Poimboeuf 
18153e7be635SJosh Poimboeuf 			ERROR_INSN(insn, "unsupported relocation in alternatives section");
1816dc419723SJosh Poimboeuf 			return -1;
1817dc419723SJosh Poimboeuf 		}
1818dc419723SJosh Poimboeuf 
1819a2296140SJosh Poimboeuf 		if (!is_static_jump(insn))
1820dcc914f4SJosh Poimboeuf 			continue;
1821dcc914f4SJosh Poimboeuf 
1822dcc914f4SJosh Poimboeuf 		if (!insn->immediate)
1823dcc914f4SJosh Poimboeuf 			continue;
1824dcc914f4SJosh Poimboeuf 
1825bfb08f22SRaphael Gault 		dest_off = arch_jump_destination(insn);
182634c861e8SJosh Poimboeuf 		if (dest_off == special_alt->new_off + special_alt->new_len) {
1827a706bb08SPeter Zijlstra 			insn->jump_dest = next_insn_same_sec(file, orig_alt_group->last_insn);
1828dcc914f4SJosh Poimboeuf 			if (!insn->jump_dest) {
18293e7be635SJosh Poimboeuf 				ERROR_INSN(insn, "can't find alternative jump destination");
1830dcc914f4SJosh Poimboeuf 				return -1;
1831dcc914f4SJosh Poimboeuf 			}
1832dcc914f4SJosh Poimboeuf 		}
183334c861e8SJosh Poimboeuf 	}
1834dcc914f4SJosh Poimboeuf 
1835dcc914f4SJosh Poimboeuf 	if (!last_new_insn) {
18363e7be635SJosh Poimboeuf 		ERROR_FUNC(special_alt->new_sec, special_alt->new_off,
18373e7be635SJosh Poimboeuf 			   "can't find last new alternative instruction");
1838dcc914f4SJosh Poimboeuf 		return -1;
1839dcc914f4SJosh Poimboeuf 	}
1840dcc914f4SJosh Poimboeuf 
1841c9c324dcSJosh Poimboeuf end:
1842b23cc71cSJosh Poimboeuf 	new_alt_group->orig_group = orig_alt_group;
1843b23cc71cSJosh Poimboeuf 	new_alt_group->first_insn = *new_insn;
18441c34496eSPeter Zijlstra 	new_alt_group->last_insn = last_new_insn;
18451c34496eSPeter Zijlstra 	new_alt_group->nop = nop;
18461154bbd3SJosh Poimboeuf 	new_alt_group->ignore = (*new_insn)->ignore_alts;
1847c9c324dcSJosh Poimboeuf 	new_alt_group->cfi = orig_alt_group->cfi;
1848dcc914f4SJosh Poimboeuf 	return 0;
1849dcc914f4SJosh Poimboeuf }
1850dcc914f4SJosh Poimboeuf 
1851dcc914f4SJosh Poimboeuf /*
1852dcc914f4SJosh Poimboeuf  * A jump table entry can either convert a nop to a jump or a jump to a nop.
1853dcc914f4SJosh Poimboeuf  * If the original instruction is a jump, make the alt entry an effective nop
1854dcc914f4SJosh Poimboeuf  * by just skipping the original instruction.
1855dcc914f4SJosh Poimboeuf  */
handle_jump_alt(struct objtool_file * file,struct special_alt * special_alt,struct instruction * orig_insn,struct instruction ** new_insn)1856dcc914f4SJosh Poimboeuf static int handle_jump_alt(struct objtool_file *file,
1857dcc914f4SJosh Poimboeuf 			   struct special_alt *special_alt,
1858dcc914f4SJosh Poimboeuf 			   struct instruction *orig_insn,
1859dcc914f4SJosh Poimboeuf 			   struct instruction **new_insn)
1860dcc914f4SJosh Poimboeuf {
186148001d26SPeter Zijlstra 	if (orig_insn->type != INSN_JUMP_UNCONDITIONAL &&
186248001d26SPeter Zijlstra 	    orig_insn->type != INSN_NOP) {
1863e2d9494bSPeter Zijlstra 
18643e7be635SJosh Poimboeuf 		ERROR_INSN(orig_insn, "unsupported instruction at jump label");
1865dcc914f4SJosh Poimboeuf 		return -1;
1866dcc914f4SJosh Poimboeuf 	}
1867dcc914f4SJosh Poimboeuf 
18684ab7674fSJosh Poimboeuf 	if (opts.hack_jump_label && special_alt->key_addend & 2) {
18696d37b83cSPeter Zijlstra 		struct reloc *reloc = insn_reloc(file, orig_insn);
18706d37b83cSPeter Zijlstra 
1871ec24b927SJosh Poimboeuf 		if (reloc)
1872ec24b927SJosh Poimboeuf 			set_reloc_type(file->elf, reloc, R_NONE);
1873c5995abeSJosh Poimboeuf 
1874c5995abeSJosh Poimboeuf 		if (elf_write_insn(file->elf, orig_insn->sec,
18756d37b83cSPeter Zijlstra 				   orig_insn->offset, orig_insn->len,
1876c5995abeSJosh Poimboeuf 				   arch_nop_insn(orig_insn->len))) {
1877c5995abeSJosh Poimboeuf 			return -1;
1878c5995abeSJosh Poimboeuf 		}
1879c5995abeSJosh Poimboeuf 
18806d37b83cSPeter Zijlstra 		orig_insn->type = INSN_NOP;
188148001d26SPeter Zijlstra 	}
188248001d26SPeter Zijlstra 
188348001d26SPeter Zijlstra 	if (orig_insn->type == INSN_NOP) {
188448001d26SPeter Zijlstra 		if (orig_insn->len == 2)
188548001d26SPeter Zijlstra 			file->jl_nop_short++;
188648001d26SPeter Zijlstra 		else
188748001d26SPeter Zijlstra 			file->jl_nop_long++;
188848001d26SPeter Zijlstra 
188948001d26SPeter Zijlstra 		return 0;
18906d37b83cSPeter Zijlstra 	}
18916d37b83cSPeter Zijlstra 
1892e2d9494bSPeter Zijlstra 	if (orig_insn->len == 2)
1893e2d9494bSPeter Zijlstra 		file->jl_short++;
1894e2d9494bSPeter Zijlstra 	else
1895e2d9494bSPeter Zijlstra 		file->jl_long++;
1896e2d9494bSPeter Zijlstra 
18971c34496eSPeter Zijlstra 	*new_insn = next_insn_same_sec(file, orig_insn);
1898dcc914f4SJosh Poimboeuf 	return 0;
1899dcc914f4SJosh Poimboeuf }
1900dcc914f4SJosh Poimboeuf 
1901dcc914f4SJosh Poimboeuf /*
1902dcc914f4SJosh Poimboeuf  * Read all the special sections which have alternate instructions which can be
1903dcc914f4SJosh Poimboeuf  * patched in or redirected to at runtime.  Each instruction having alternate
1904dcc914f4SJosh Poimboeuf  * instruction(s) has them added to its insn->alts list, which will be
1905dcc914f4SJosh Poimboeuf  * traversed in validate_branch().
1906dcc914f4SJosh Poimboeuf  */
add_special_section_alts(struct objtool_file * file)1907dcc914f4SJosh Poimboeuf static int add_special_section_alts(struct objtool_file *file)
1908dcc914f4SJosh Poimboeuf {
1909dcc914f4SJosh Poimboeuf 	struct list_head special_alts;
1910dcc914f4SJosh Poimboeuf 	struct instruction *orig_insn, *new_insn;
1911dcc914f4SJosh Poimboeuf 	struct special_alt *special_alt, *tmp;
1912dcc914f4SJosh Poimboeuf 	struct alternative *alt;
1913dcc914f4SJosh Poimboeuf 	int ret;
1914dcc914f4SJosh Poimboeuf 
1915c5995abeSJosh Poimboeuf 	if (special_get_alts(file->elf, &special_alts))
1916c5995abeSJosh Poimboeuf 		return -1;
1917dcc914f4SJosh Poimboeuf 
1918dcc914f4SJosh Poimboeuf 	list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {
1919dcc914f4SJosh Poimboeuf 
1920dcc914f4SJosh Poimboeuf 		orig_insn = find_insn(file, special_alt->orig_sec,
1921dcc914f4SJosh Poimboeuf 				      special_alt->orig_off);
1922dcc914f4SJosh Poimboeuf 		if (!orig_insn) {
19233e7be635SJosh Poimboeuf 			ERROR_FUNC(special_alt->orig_sec, special_alt->orig_off,
19243e7be635SJosh Poimboeuf 				   "special: can't find orig instruction");
1925c5995abeSJosh Poimboeuf 			return -1;
1926dcc914f4SJosh Poimboeuf 		}
1927dcc914f4SJosh Poimboeuf 
1928dcc914f4SJosh Poimboeuf 		new_insn = NULL;
1929dcc914f4SJosh Poimboeuf 		if (!special_alt->group || special_alt->new_len) {
1930dcc914f4SJosh Poimboeuf 			new_insn = find_insn(file, special_alt->new_sec,
1931dcc914f4SJosh Poimboeuf 					     special_alt->new_off);
1932dcc914f4SJosh Poimboeuf 			if (!new_insn) {
19333e7be635SJosh Poimboeuf 				ERROR_FUNC(special_alt->new_sec, special_alt->new_off,
19343e7be635SJosh Poimboeuf 					   "special: can't find new instruction");
1935c5995abeSJosh Poimboeuf 				return -1;
1936dcc914f4SJosh Poimboeuf 			}
1937dcc914f4SJosh Poimboeuf 		}
1938dcc914f4SJosh Poimboeuf 
1939dcc914f4SJosh Poimboeuf 		if (special_alt->group) {
19407170cf47SJulien Thierry 			if (!special_alt->orig_len) {
19413e7be635SJosh Poimboeuf 				ERROR_INSN(orig_insn, "empty alternative entry");
19427170cf47SJulien Thierry 				continue;
19437170cf47SJulien Thierry 			}
19447170cf47SJulien Thierry 
1945dcc914f4SJosh Poimboeuf 			ret = handle_group_alt(file, special_alt, orig_insn,
1946dcc914f4SJosh Poimboeuf 					       &new_insn);
1947dcc914f4SJosh Poimboeuf 			if (ret)
1948c5995abeSJosh Poimboeuf 				return ret;
1949c5995abeSJosh Poimboeuf 
1950dcc914f4SJosh Poimboeuf 		} else if (special_alt->jump_or_nop) {
1951dcc914f4SJosh Poimboeuf 			ret = handle_jump_alt(file, special_alt, orig_insn,
1952dcc914f4SJosh Poimboeuf 					      &new_insn);
1953dcc914f4SJosh Poimboeuf 			if (ret)
1954c5995abeSJosh Poimboeuf 				return ret;
1955dcc914f4SJosh Poimboeuf 		}
1956dcc914f4SJosh Poimboeuf 
1957c5995abeSJosh Poimboeuf 		alt = calloc(1, sizeof(*alt));
1958258c7605SJosh Poimboeuf 		if (!alt) {
19593e7be635SJosh Poimboeuf 			ERROR_GLIBC("calloc");
1960c5995abeSJosh Poimboeuf 			return -1;
1961258c7605SJosh Poimboeuf 		}
1962258c7605SJosh Poimboeuf 
1963dcc914f4SJosh Poimboeuf 		alt->insn = new_insn;
1964d5406654SPeter Zijlstra 		alt->next = orig_insn->alts;
1965d5406654SPeter Zijlstra 		orig_insn->alts = alt;
1966dcc914f4SJosh Poimboeuf 
1967dcc914f4SJosh Poimboeuf 		list_del(&special_alt->list);
1968dcc914f4SJosh Poimboeuf 		free(special_alt);
1969dcc914f4SJosh Poimboeuf 	}
1970dcc914f4SJosh Poimboeuf 
19712daf7fabSJosh Poimboeuf 	if (opts.stats) {
1972e2d9494bSPeter Zijlstra 		printf("jl\\\tNOP\tJMP\n");
1973e2d9494bSPeter Zijlstra 		printf("short:\t%ld\t%ld\n", file->jl_nop_short, file->jl_short);
1974e2d9494bSPeter Zijlstra 		printf("long:\t%ld\t%ld\n", file->jl_nop_long, file->jl_long);
1975e2d9494bSPeter Zijlstra 	}
1976e2d9494bSPeter Zijlstra 
1977c5995abeSJosh Poimboeuf 	return 0;
1978dcc914f4SJosh Poimboeuf }
1979dcc914f4SJosh Poimboeuf 
arch_jump_table_sym_offset(struct reloc * reloc,struct reloc * table)1980c4b93b06STiezhu Yang __weak unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table)
1981c4b93b06STiezhu Yang {
1982c4b93b06STiezhu Yang 	return reloc->sym->offset + reloc_addend(reloc);
1983c4b93b06STiezhu Yang }
1984c4b93b06STiezhu Yang 
add_jump_table(struct objtool_file * file,struct instruction * insn)1985ef753d66SJosh Poimboeuf static int add_jump_table(struct objtool_file *file, struct instruction *insn)
1986dcc914f4SJosh Poimboeuf {
1987c3cb6c15SArd Biesheuvel 	unsigned long table_size = insn_jump_table_size(insn);
1988dbcdbdfdSPeter Zijlstra 	struct symbol *pfunc = insn_func(insn)->pfunc;
1989be2f0b1eSJosh Poimboeuf 	struct reloc *table = insn_jump_table(insn);
1990be2f0b1eSJosh Poimboeuf 	struct instruction *dest_insn;
1991fd35c88bSJosh Poimboeuf 	unsigned int prev_offset = 0;
1992be2f0b1eSJosh Poimboeuf 	struct reloc *reloc = table;
1993be2f0b1eSJosh Poimboeuf 	struct alternative *alt;
1994ab6ce22bSTiezhu Yang 	unsigned long sym_offset;
1995dcc914f4SJosh Poimboeuf 
1996e7c2bc37SJosh Poimboeuf 	/*
1997f1974222SMatt Helsley 	 * Each @reloc is a switch table relocation which points to the target
1998e7c2bc37SJosh Poimboeuf 	 * instruction.
1999e7c2bc37SJosh Poimboeuf 	 */
2000caa4a6b7SJosh Poimboeuf 	for_each_reloc_from(table->sec, reloc) {
2001bd98c813SJann Horn 
2002bd98c813SJann Horn 		/* Check for the end of the table: */
2003c3cb6c15SArd Biesheuvel 		if (table_size && reloc_offset(reloc) - reloc_offset(table) >= table_size)
2004c3cb6c15SArd Biesheuvel 			break;
2005ef753d66SJosh Poimboeuf 		if (reloc != table && is_jump_table(reloc))
2006dcc914f4SJosh Poimboeuf 			break;
2007dcc914f4SJosh Poimboeuf 
2008e7c2bc37SJosh Poimboeuf 		/* Make sure the table entries are consecutive: */
2009091bf313STiezhu Yang 		if (prev_offset && reloc_offset(reloc) != prev_offset + arch_reloc_size(reloc))
2010fd35c88bSJosh Poimboeuf 			break;
2011fd35c88bSJosh Poimboeuf 
2012c4b93b06STiezhu Yang 		sym_offset = arch_jump_table_sym_offset(reloc, table);
2013ab6ce22bSTiezhu Yang 
2014fd35c88bSJosh Poimboeuf 		/* Detect function pointers from contiguous objects: */
2015ab6ce22bSTiezhu Yang 		if (reloc->sym->sec == pfunc->sec && sym_offset == pfunc->offset)
2016fd35c88bSJosh Poimboeuf 			break;
2017fd35c88bSJosh Poimboeuf 
20183724062cSJosh Poimboeuf 		/*
20193724062cSJosh Poimboeuf 		 * Clang sometimes leaves dangling unused jump table entries
20203724062cSJosh Poimboeuf 		 * which point to the end of the function.  Ignore them.
20213724062cSJosh Poimboeuf 		 */
20223724062cSJosh Poimboeuf 		if (reloc->sym->sec == pfunc->sec &&
2023ab6ce22bSTiezhu Yang 		    sym_offset == pfunc->offset + pfunc->len)
20243724062cSJosh Poimboeuf 			goto next;
20253724062cSJosh Poimboeuf 
2026ab6ce22bSTiezhu Yang 		dest_insn = find_insn(file, reloc->sym->sec, sym_offset);
2027e7c2bc37SJosh Poimboeuf 		if (!dest_insn)
2028dcc914f4SJosh Poimboeuf 			break;
2029dcc914f4SJosh Poimboeuf 
2030e7c2bc37SJosh Poimboeuf 		/* Make sure the destination is in the same function: */
2031dbcdbdfdSPeter Zijlstra 		if (!insn_func(dest_insn) || insn_func(dest_insn)->pfunc != pfunc)
203213810435SJosh Poimboeuf 			break;
2033dcc914f4SJosh Poimboeuf 
2034c5995abeSJosh Poimboeuf 		alt = calloc(1, sizeof(*alt));
2035dcc914f4SJosh Poimboeuf 		if (!alt) {
20363e7be635SJosh Poimboeuf 			ERROR_GLIBC("calloc");
2037dcc914f4SJosh Poimboeuf 			return -1;
2038dcc914f4SJosh Poimboeuf 		}
2039dcc914f4SJosh Poimboeuf 
2040e7c2bc37SJosh Poimboeuf 		alt->insn = dest_insn;
2041d5406654SPeter Zijlstra 		alt->next = insn->alts;
2042d5406654SPeter Zijlstra 		insn->alts = alt;
20433724062cSJosh Poimboeuf next:
2044e4cbb9b8SJosh Poimboeuf 		prev_offset = reloc_offset(reloc);
2045fd35c88bSJosh Poimboeuf 	}
2046fd35c88bSJosh Poimboeuf 
2047fd35c88bSJosh Poimboeuf 	if (!prev_offset) {
20483e7be635SJosh Poimboeuf 		ERROR_INSN(insn, "can't find switch jump table");
2049fd35c88bSJosh Poimboeuf 		return -1;
2050dcc914f4SJosh Poimboeuf 	}
2051dcc914f4SJosh Poimboeuf 
2052dcc914f4SJosh Poimboeuf 	return 0;
2053dcc914f4SJosh Poimboeuf }
2054dcc914f4SJosh Poimboeuf 
2055dcc914f4SJosh Poimboeuf /*
2056d871f7b5SRaphael Gault  * find_jump_table() - Given a dynamic jump, find the switch jump table
2057d871f7b5SRaphael Gault  * associated with it.
2058dcc914f4SJosh Poimboeuf  */
find_jump_table(struct objtool_file * file,struct symbol * func,struct instruction * insn)2059c3cb6c15SArd Biesheuvel static void find_jump_table(struct objtool_file *file, struct symbol *func,
2060dcc914f4SJosh Poimboeuf 			    struct instruction *insn)
2061dcc914f4SJosh Poimboeuf {
2062d871f7b5SRaphael Gault 	struct reloc *table_reloc;
2063113d4bc9SJosh Poimboeuf 	struct instruction *dest_insn, *orig_insn = insn;
2064c3cb6c15SArd Biesheuvel 	unsigned long table_size;
2065ab6ce22bSTiezhu Yang 	unsigned long sym_offset;
2066dcc914f4SJosh Poimboeuf 
206799ce7962SPeter Zijlstra 	/*
206899ce7962SPeter Zijlstra 	 * Backward search using the @first_jump_src links, these help avoid
206999ce7962SPeter Zijlstra 	 * much of the 'in between' code. Which avoids us getting confused by
207099ce7962SPeter Zijlstra 	 * it.
207199ce7962SPeter Zijlstra 	 */
20727dec80ccSJosh Poimboeuf 	for (;
2073dbcdbdfdSPeter Zijlstra 	     insn && insn_func(insn) && insn_func(insn)->pfunc == func;
20741119d265SJosh Poimboeuf 	     insn = insn->first_jump_src ?: prev_insn_same_sym(file, insn)) {
207599ce7962SPeter Zijlstra 
20767dec80ccSJosh Poimboeuf 		if (insn != orig_insn && insn->type == INSN_JUMP_DYNAMIC)
2077dcc914f4SJosh Poimboeuf 			break;
2078dcc914f4SJosh Poimboeuf 
2079dcc914f4SJosh Poimboeuf 		/* allow small jumps within the range */
2080dcc914f4SJosh Poimboeuf 		if (insn->type == INSN_JUMP_UNCONDITIONAL &&
2081dcc914f4SJosh Poimboeuf 		    insn->jump_dest &&
2082dcc914f4SJosh Poimboeuf 		    (insn->jump_dest->offset <= insn->offset ||
2083dcc914f4SJosh Poimboeuf 		     insn->jump_dest->offset > orig_insn->offset))
2084dcc914f4SJosh Poimboeuf 			break;
2085dcc914f4SJosh Poimboeuf 
2086c3cb6c15SArd Biesheuvel 		table_reloc = arch_find_switch_table(file, insn, &table_size);
2087f1974222SMatt Helsley 		if (!table_reloc)
2088e7c2bc37SJosh Poimboeuf 			continue;
2089ab6ce22bSTiezhu Yang 
2090ab6ce22bSTiezhu Yang 		sym_offset = table_reloc->sym->offset + reloc_addend(table_reloc);
2091ab6ce22bSTiezhu Yang 
2092ab6ce22bSTiezhu Yang 		dest_insn = find_insn(file, table_reloc->sym->sec, sym_offset);
2093dbcdbdfdSPeter Zijlstra 		if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func)
2094113d4bc9SJosh Poimboeuf 			continue;
2095e7c2bc37SJosh Poimboeuf 
2096ef753d66SJosh Poimboeuf 		set_jump_table(table_reloc);
2097c3cb6c15SArd Biesheuvel 		orig_insn->_jump_table = table_reloc;
2098c3cb6c15SArd Biesheuvel 		orig_insn->_jump_table_size = table_size;
2099ef753d66SJosh Poimboeuf 
2100c3cb6c15SArd Biesheuvel 		break;
21017dec80ccSJosh Poimboeuf 	}
2102dcc914f4SJosh Poimboeuf }
2103dcc914f4SJosh Poimboeuf 
2104bd98c813SJann Horn /*
2105bd98c813SJann Horn  * First pass: Mark the head of each jump table so that in the next pass,
2106bd98c813SJann Horn  * we know when a given jump table ends and the next one starts.
2107bd98c813SJann Horn  */
mark_func_jump_tables(struct objtool_file * file,struct symbol * func)2108bd98c813SJann Horn static void mark_func_jump_tables(struct objtool_file *file,
2109dcc914f4SJosh Poimboeuf 				    struct symbol *func)
2110dcc914f4SJosh Poimboeuf {
2111bd98c813SJann Horn 	struct instruction *insn, *last = NULL;
2112dcc914f4SJosh Poimboeuf 
2113f0f70adbSPeter Zijlstra 	func_for_each_insn(file, func, insn) {
211499ce7962SPeter Zijlstra 		if (!last)
211599ce7962SPeter Zijlstra 			last = insn;
211699ce7962SPeter Zijlstra 
211799ce7962SPeter Zijlstra 		/*
211899ce7962SPeter Zijlstra 		 * Store back-pointers for unconditional forward jumps such
2119e7c2bc37SJosh Poimboeuf 		 * that find_jump_table() can back-track using those and
212099ce7962SPeter Zijlstra 		 * avoid some potentially confusing code.
212199ce7962SPeter Zijlstra 		 */
212299ce7962SPeter Zijlstra 		if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest &&
212399ce7962SPeter Zijlstra 		    insn->offset > last->offset &&
212499ce7962SPeter Zijlstra 		    insn->jump_dest->offset > insn->offset &&
212599ce7962SPeter Zijlstra 		    !insn->jump_dest->first_jump_src) {
212699ce7962SPeter Zijlstra 
212799ce7962SPeter Zijlstra 			insn->jump_dest->first_jump_src = insn;
212899ce7962SPeter Zijlstra 			last = insn->jump_dest;
212999ce7962SPeter Zijlstra 		}
213099ce7962SPeter Zijlstra 
2131dcc914f4SJosh Poimboeuf 		if (insn->type != INSN_JUMP_DYNAMIC)
2132dcc914f4SJosh Poimboeuf 			continue;
2133dcc914f4SJosh Poimboeuf 
2134c3cb6c15SArd Biesheuvel 		find_jump_table(file, func, insn);
2135bd98c813SJann Horn 	}
2136bd98c813SJann Horn }
2137bd98c813SJann Horn 
add_func_jump_tables(struct objtool_file * file,struct symbol * func)2138bd98c813SJann Horn static int add_func_jump_tables(struct objtool_file *file,
2139bd98c813SJann Horn 				  struct symbol *func)
2140bd98c813SJann Horn {
2141ef753d66SJosh Poimboeuf 	struct instruction *insn;
2142ef753d66SJosh Poimboeuf 	int ret;
2143bd98c813SJann Horn 
2144f0f70adbSPeter Zijlstra 	func_for_each_insn(file, func, insn) {
2145c6f5dc28SPeter Zijlstra 		if (!insn_jump_table(insn))
2146dcc914f4SJosh Poimboeuf 			continue;
2147dcc914f4SJosh Poimboeuf 
2148ef753d66SJosh Poimboeuf 		ret = add_jump_table(file, insn);
2149be2f0b1eSJosh Poimboeuf 		if (ret)
2150be2f0b1eSJosh Poimboeuf 			return ret;
2151be2f0b1eSJosh Poimboeuf 	}
2152be2f0b1eSJosh Poimboeuf 
2153ef753d66SJosh Poimboeuf 	return 0;
2154dcc914f4SJosh Poimboeuf }
2155dcc914f4SJosh Poimboeuf 
2156dcc914f4SJosh Poimboeuf /*
2157dcc914f4SJosh Poimboeuf  * For some switch statements, gcc generates a jump table in the .rodata
2158dcc914f4SJosh Poimboeuf  * section which contains a list of addresses within the function to jump to.
2159dcc914f4SJosh Poimboeuf  * This finds these jump tables and adds them to the insn->alts lists.
2160dcc914f4SJosh Poimboeuf  */
add_jump_table_alts(struct objtool_file * file)2161e7c2bc37SJosh Poimboeuf static int add_jump_table_alts(struct objtool_file *file)
2162dcc914f4SJosh Poimboeuf {
2163dcc914f4SJosh Poimboeuf 	struct symbol *func;
2164dcc914f4SJosh Poimboeuf 	int ret;
2165dcc914f4SJosh Poimboeuf 
21664a60aa05SAllan Xavier 	if (!file->rodata)
2167dcc914f4SJosh Poimboeuf 		return 0;
2168dcc914f4SJosh Poimboeuf 
21699290e772SJosh Poimboeuf 	for_each_sym(file, func) {
2170dcc914f4SJosh Poimboeuf 		if (func->type != STT_FUNC)
2171dcc914f4SJosh Poimboeuf 			continue;
2172dcc914f4SJosh Poimboeuf 
2173bd98c813SJann Horn 		mark_func_jump_tables(file, func);
2174e7c2bc37SJosh Poimboeuf 		ret = add_func_jump_tables(file, func);
2175dcc914f4SJosh Poimboeuf 		if (ret)
2176dcc914f4SJosh Poimboeuf 			return ret;
2177dcc914f4SJosh Poimboeuf 	}
2178dcc914f4SJosh Poimboeuf 
2179dcc914f4SJosh Poimboeuf 	return 0;
2180dcc914f4SJosh Poimboeuf }
2181dcc914f4SJosh Poimboeuf 
set_func_state(struct cfi_state * state)2182b735bd3eSJosh Poimboeuf static void set_func_state(struct cfi_state *state)
2183b735bd3eSJosh Poimboeuf {
2184b735bd3eSJosh Poimboeuf 	state->cfa = initial_func_cfi.cfa;
2185b735bd3eSJosh Poimboeuf 	memcpy(&state->regs, &initial_func_cfi.regs,
2186b735bd3eSJosh Poimboeuf 	       CFI_NUM_REGS * sizeof(struct cfi_reg));
2187b735bd3eSJosh Poimboeuf 	state->stack_size = initial_func_cfi.cfa.offset;
2188fb799447SJosh Poimboeuf 	state->type = UNWIND_HINT_TYPE_CALL;
2189b735bd3eSJosh Poimboeuf }
2190b735bd3eSJosh Poimboeuf 
read_unwind_hints(struct objtool_file * file)219139358a03SJosh Poimboeuf static int read_unwind_hints(struct objtool_file *file)
219239358a03SJosh Poimboeuf {
21938b946cc3SPeter Zijlstra 	struct cfi_state cfi = init_cfi;
2194a5bd6236SJosh Poimboeuf 	struct section *sec;
219539358a03SJosh Poimboeuf 	struct unwind_hint *hint;
219639358a03SJosh Poimboeuf 	struct instruction *insn;
21978b946cc3SPeter Zijlstra 	struct reloc *reloc;
2198e91c5e4cSTiezhu Yang 	unsigned long offset;
219939358a03SJosh Poimboeuf 	int i;
220039358a03SJosh Poimboeuf 
220139358a03SJosh Poimboeuf 	sec = find_section_by_name(file->elf, ".discard.unwind_hints");
220239358a03SJosh Poimboeuf 	if (!sec)
220339358a03SJosh Poimboeuf 		return 0;
220439358a03SJosh Poimboeuf 
2205a5bd6236SJosh Poimboeuf 	if (!sec->rsec) {
22063e7be635SJosh Poimboeuf 		ERROR("missing .rela.discard.unwind_hints section");
220739358a03SJosh Poimboeuf 		return -1;
220839358a03SJosh Poimboeuf 	}
220939358a03SJosh Poimboeuf 
2210fe255fe6SJoe Lawrence 	if (sec->sh.sh_size % sizeof(struct unwind_hint)) {
22113e7be635SJosh Poimboeuf 		ERROR("struct unwind_hint size mismatch");
221239358a03SJosh Poimboeuf 		return -1;
221339358a03SJosh Poimboeuf 	}
221439358a03SJosh Poimboeuf 
221539358a03SJosh Poimboeuf 	file->hints = true;
221639358a03SJosh Poimboeuf 
2217fe255fe6SJoe Lawrence 	for (i = 0; i < sec->sh.sh_size / sizeof(struct unwind_hint); i++) {
221839358a03SJosh Poimboeuf 		hint = (struct unwind_hint *)sec->data->d_buf + i;
221939358a03SJosh Poimboeuf 
2220f1974222SMatt Helsley 		reloc = find_reloc_by_dest(file->elf, sec, i * sizeof(*hint));
2221f1974222SMatt Helsley 		if (!reloc) {
22223e7be635SJosh Poimboeuf 			ERROR("can't find reloc for unwind_hints[%d]", i);
222339358a03SJosh Poimboeuf 			return -1;
222439358a03SJosh Poimboeuf 		}
222539358a03SJosh Poimboeuf 
2226e91c5e4cSTiezhu Yang 		if (reloc->sym->type == STT_SECTION) {
2227e91c5e4cSTiezhu Yang 			offset = reloc_addend(reloc);
2228e91c5e4cSTiezhu Yang 		} else if (reloc->sym->local_label) {
2229e91c5e4cSTiezhu Yang 			offset = reloc->sym->offset;
2230e91c5e4cSTiezhu Yang 		} else {
22313e7be635SJosh Poimboeuf 			ERROR("unexpected relocation symbol type in %s", sec->rsec->name);
2232e91c5e4cSTiezhu Yang 			return -1;
2233e91c5e4cSTiezhu Yang 		}
2234e91c5e4cSTiezhu Yang 
2235e91c5e4cSTiezhu Yang 		insn = find_insn(file, reloc->sym->sec, offset);
223639358a03SJosh Poimboeuf 		if (!insn) {
22373e7be635SJosh Poimboeuf 			ERROR("can't find insn for unwind_hints[%d]", i);
223839358a03SJosh Poimboeuf 			return -1;
223939358a03SJosh Poimboeuf 		}
224039358a03SJosh Poimboeuf 
2241b735bd3eSJosh Poimboeuf 		insn->hint = true;
224239358a03SJosh Poimboeuf 
22431e4b6191SJosh Poimboeuf 		if (hint->type == UNWIND_HINT_TYPE_UNDEFINED) {
22441e4b6191SJosh Poimboeuf 			insn->cfi = &force_undefined_cfi;
22451e4b6191SJosh Poimboeuf 			continue;
22461e4b6191SJosh Poimboeuf 		}
22471e4b6191SJosh Poimboeuf 
22488faea26eSJosh Poimboeuf 		if (hint->type == UNWIND_HINT_TYPE_SAVE) {
22498faea26eSJosh Poimboeuf 			insn->hint = false;
22508faea26eSJosh Poimboeuf 			insn->save = true;
22518faea26eSJosh Poimboeuf 			continue;
22528faea26eSJosh Poimboeuf 		}
22538faea26eSJosh Poimboeuf 
22548faea26eSJosh Poimboeuf 		if (hint->type == UNWIND_HINT_TYPE_RESTORE) {
22558faea26eSJosh Poimboeuf 			insn->restore = true;
22568faea26eSJosh Poimboeuf 			continue;
22578faea26eSJosh Poimboeuf 		}
22588faea26eSJosh Poimboeuf 
2259a09a6e23SPeter Zijlstra 		if (hint->type == UNWIND_HINT_TYPE_REGS_PARTIAL) {
226008f87a93SPeter Zijlstra 			struct symbol *sym = find_symbol_by_offset(insn->sec, insn->offset);
226108f87a93SPeter Zijlstra 
2262a09a6e23SPeter Zijlstra 			if (sym && sym->bind == STB_GLOBAL) {
2263a09a6e23SPeter Zijlstra 				if (opts.ibt && insn->type != INSN_ENDBR && !insn->noendbr) {
22643e7be635SJosh Poimboeuf 					ERROR_INSN(insn, "UNWIND_HINT_IRET_REGS without ENDBR");
2265c5995abeSJosh Poimboeuf 					return -1;
2266a09a6e23SPeter Zijlstra 				}
2267a09a6e23SPeter Zijlstra 			}
226808f87a93SPeter Zijlstra 		}
226908f87a93SPeter Zijlstra 
2270b735bd3eSJosh Poimboeuf 		if (hint->type == UNWIND_HINT_TYPE_FUNC) {
22718b946cc3SPeter Zijlstra 			insn->cfi = &func_cfi;
227239358a03SJosh Poimboeuf 			continue;
227339358a03SJosh Poimboeuf 		}
227439358a03SJosh Poimboeuf 
22758b946cc3SPeter Zijlstra 		if (insn->cfi)
22768b946cc3SPeter Zijlstra 			cfi = *(insn->cfi);
22778b946cc3SPeter Zijlstra 
22788b946cc3SPeter Zijlstra 		if (arch_decode_hint_reg(hint->sp_reg, &cfi.cfa.base)) {
22793e7be635SJosh Poimboeuf 			ERROR_INSN(insn, "unsupported unwind_hint sp base reg %d", hint->sp_reg);
228039358a03SJosh Poimboeuf 			return -1;
228139358a03SJosh Poimboeuf 		}
228239358a03SJosh Poimboeuf 
22830646c28bSChristophe Leroy 		cfi.cfa.offset = bswap_if_needed(file->elf, hint->sp_offset);
22848b946cc3SPeter Zijlstra 		cfi.type = hint->type;
228500c8f01cSJosh Poimboeuf 		cfi.signal = hint->signal;
22868b946cc3SPeter Zijlstra 
22878b946cc3SPeter Zijlstra 		insn->cfi = cfi_hash_find_or_add(&cfi);
228839358a03SJosh Poimboeuf 	}
228939358a03SJosh Poimboeuf 
229039358a03SJosh Poimboeuf 	return 0;
229139358a03SJosh Poimboeuf }
229239358a03SJosh Poimboeuf 
read_annotate(struct objtool_file * file,int (* func)(struct objtool_file * file,int type,struct instruction * insn))2293112765caSPeter Zijlstra static int read_annotate(struct objtool_file *file,
2294112765caSPeter Zijlstra 			 int (*func)(struct objtool_file *file, int type, struct instruction *insn))
229596db4a98SPeter Zijlstra {
22962116b349SPeter Zijlstra 	struct section *sec;
229796db4a98SPeter Zijlstra 	struct instruction *insn;
229896db4a98SPeter Zijlstra 	struct reloc *reloc;
2299e7a174fbSPeter Zijlstra 	uint64_t offset;
2300bf5febebSPeter Zijlstra 	int type, ret;
230196db4a98SPeter Zijlstra 
23022116b349SPeter Zijlstra 	sec = find_section_by_name(file->elf, ".discard.annotate_insn");
23032116b349SPeter Zijlstra 	if (!sec)
230496db4a98SPeter Zijlstra 		return 0;
230596db4a98SPeter Zijlstra 
23062116b349SPeter Zijlstra 	if (!sec->rsec)
23072116b349SPeter Zijlstra 		return 0;
23082116b349SPeter Zijlstra 
23092116b349SPeter Zijlstra 	if (sec->sh.sh_entsize != 8) {
23102116b349SPeter Zijlstra 		static bool warned = false;
23117e501637SPeter Zijlstra 		if (!warned && opts.verbose) {
23122116b349SPeter Zijlstra 			WARN("%s: dodgy linker, sh_entsize != 8", sec->name);
23132116b349SPeter Zijlstra 			warned = true;
23142116b349SPeter Zijlstra 		}
23152116b349SPeter Zijlstra 		sec->sh.sh_entsize = 8;
23162116b349SPeter Zijlstra 	}
23172116b349SPeter Zijlstra 
23182116b349SPeter Zijlstra 	for_each_reloc(sec->rsec, reloc) {
23192116b349SPeter Zijlstra 		type = *(u32 *)(sec->data->d_buf + (reloc_idx(reloc) * sec->sh.sh_entsize) + 4);
23202116b349SPeter Zijlstra 
2321e7a174fbSPeter Zijlstra 		offset = reloc->sym->offset + reloc_addend(reloc);
2322e7a174fbSPeter Zijlstra 		insn = find_insn(file, reloc->sym->sec, offset);
2323e7a174fbSPeter Zijlstra 
232496db4a98SPeter Zijlstra 		if (!insn) {
23253e7be635SJosh Poimboeuf 			ERROR("bad .discard.annotate_insn entry: %d of type %d", reloc_idx(reloc), type);
232696db4a98SPeter Zijlstra 			return -1;
232796db4a98SPeter Zijlstra 		}
232896db4a98SPeter Zijlstra 
2329112765caSPeter Zijlstra 		ret = func(file, type, insn);
2330bf5febebSPeter Zijlstra 		if (ret < 0)
2331bf5febebSPeter Zijlstra 			return ret;
23322116b349SPeter Zijlstra 	}
23332116b349SPeter Zijlstra 
23342116b349SPeter Zijlstra 	return 0;
23352116b349SPeter Zijlstra }
23362116b349SPeter Zijlstra 
__annotate_early(struct objtool_file * file,int type,struct instruction * insn)2337a8a330ddSPeter Zijlstra static int __annotate_early(struct objtool_file *file, int type, struct instruction *insn)
2338f0cd57c3SPeter Zijlstra {
2339a8a330ddSPeter Zijlstra 	switch (type) {
23401154bbd3SJosh Poimboeuf 
23411154bbd3SJosh Poimboeuf 	/* Must be before add_special_section_alts() */
2342a8a330ddSPeter Zijlstra 	case ANNOTYPE_IGNORE_ALTS:
2343f0cd57c3SPeter Zijlstra 		insn->ignore_alts = true;
2344a8a330ddSPeter Zijlstra 		break;
2345a8a330ddSPeter Zijlstra 
2346a8a330ddSPeter Zijlstra 	/*
2347a8a330ddSPeter Zijlstra 	 * Must be before read_unwind_hints() since that needs insn->noendbr.
2348a8a330ddSPeter Zijlstra 	 */
2349a8a330ddSPeter Zijlstra 	case ANNOTYPE_NOENDBR:
235096db4a98SPeter Zijlstra 		insn->noendbr = 1;
2351a8a330ddSPeter Zijlstra 		break;
2352a8a330ddSPeter Zijlstra 
2353a8a330ddSPeter Zijlstra 	default:
2354a8a330ddSPeter Zijlstra 		break;
235596db4a98SPeter Zijlstra 	}
235696db4a98SPeter Zijlstra 
235796db4a98SPeter Zijlstra 	return 0;
235896db4a98SPeter Zijlstra }
235996db4a98SPeter Zijlstra 
__annotate_ifc(struct objtool_file * file,int type,struct instruction * insn)2360112765caSPeter Zijlstra static int __annotate_ifc(struct objtool_file *file, int type, struct instruction *insn)
2361b5bc2231SPeter Zijlstra {
23628aa8eb2aSAlexandre Chartre 	unsigned long dest_off;
23638aa8eb2aSAlexandre Chartre 
2364112765caSPeter Zijlstra 	if (type != ANNOTYPE_INTRA_FUNCTION_CALL)
2365112765caSPeter Zijlstra 		return 0;
23668aa8eb2aSAlexandre Chartre 
23678aa8eb2aSAlexandre Chartre 	if (insn->type != INSN_CALL) {
23683e7be635SJosh Poimboeuf 		ERROR_INSN(insn, "intra_function_call not a direct call");
23698aa8eb2aSAlexandre Chartre 		return -1;
23708aa8eb2aSAlexandre Chartre 	}
23718aa8eb2aSAlexandre Chartre 
23728aa8eb2aSAlexandre Chartre 	/*
23738aa8eb2aSAlexandre Chartre 	 * Treat intra-function CALLs as JMPs, but with a stack_op.
23748aa8eb2aSAlexandre Chartre 	 * See add_call_destinations(), which strips stack_ops from
23758aa8eb2aSAlexandre Chartre 	 * normal CALLs.
23768aa8eb2aSAlexandre Chartre 	 */
23778aa8eb2aSAlexandre Chartre 	insn->type = INSN_JUMP_UNCONDITIONAL;
23788aa8eb2aSAlexandre Chartre 
23797b3e3186SChen Zhongjin 	dest_off = arch_jump_destination(insn);
23808aa8eb2aSAlexandre Chartre 	insn->jump_dest = find_insn(file, insn->sec, dest_off);
23818aa8eb2aSAlexandre Chartre 	if (!insn->jump_dest) {
23823e7be635SJosh Poimboeuf 		ERROR_INSN(insn, "can't find call dest at %s+0x%lx",
23838aa8eb2aSAlexandre Chartre 			   insn->sec->name, dest_off);
23848aa8eb2aSAlexandre Chartre 		return -1;
23858aa8eb2aSAlexandre Chartre 	}
2386112765caSPeter Zijlstra 
2387112765caSPeter Zijlstra 	return 0;
2388112765caSPeter Zijlstra }
2389112765caSPeter Zijlstra 
__annotate_late(struct objtool_file * file,int type,struct instruction * insn)2390a8a330ddSPeter Zijlstra static int __annotate_late(struct objtool_file *file, int type, struct instruction *insn)
2391112765caSPeter Zijlstra {
2392a8a330ddSPeter Zijlstra 	switch (type) {
2393e7e0eb53SPeter Zijlstra 	case ANNOTYPE_NOENDBR:
2394e7e0eb53SPeter Zijlstra 		/* early */
2395e7e0eb53SPeter Zijlstra 		break;
2396e7e0eb53SPeter Zijlstra 
2397a8a330ddSPeter Zijlstra 	case ANNOTYPE_RETPOLINE_SAFE:
2398112765caSPeter Zijlstra 		if (insn->type != INSN_JUMP_DYNAMIC &&
2399112765caSPeter Zijlstra 		    insn->type != INSN_CALL_DYNAMIC &&
2400112765caSPeter Zijlstra 		    insn->type != INSN_RETURN &&
2401112765caSPeter Zijlstra 		    insn->type != INSN_NOP) {
24023e7be635SJosh Poimboeuf 			ERROR_INSN(insn, "retpoline_safe hint not an indirect jump/call/ret/nop");
2403112765caSPeter Zijlstra 			return -1;
2404112765caSPeter Zijlstra 		}
2405112765caSPeter Zijlstra 
2406112765caSPeter Zijlstra 		insn->retpoline_safe = true;
2407a8a330ddSPeter Zijlstra 		break;
2408112765caSPeter Zijlstra 
2409112765caSPeter Zijlstra 	case ANNOTYPE_INSTR_BEGIN:
2410112765caSPeter Zijlstra 		insn->instr++;
2411112765caSPeter Zijlstra 		break;
2412112765caSPeter Zijlstra 
2413112765caSPeter Zijlstra 	case ANNOTYPE_INSTR_END:
2414112765caSPeter Zijlstra 		insn->instr--;
2415112765caSPeter Zijlstra 		break;
2416112765caSPeter Zijlstra 
2417a8a330ddSPeter Zijlstra 	case ANNOTYPE_UNRET_BEGIN:
2418a8a330ddSPeter Zijlstra 		insn->unret = 1;
2419a8a330ddSPeter Zijlstra 		break;
2420a8a330ddSPeter Zijlstra 
2421e7e0eb53SPeter Zijlstra 	case ANNOTYPE_IGNORE_ALTS:
2422e7e0eb53SPeter Zijlstra 		/* early */
2423e7e0eb53SPeter Zijlstra 		break;
2424e7e0eb53SPeter Zijlstra 
2425e7e0eb53SPeter Zijlstra 	case ANNOTYPE_INTRA_FUNCTION_CALL:
2426e7e0eb53SPeter Zijlstra 		/* ifc */
2427e7e0eb53SPeter Zijlstra 		break;
2428e7e0eb53SPeter Zijlstra 
2429e7a174fbSPeter Zijlstra 	case ANNOTYPE_REACHABLE:
2430e7a174fbSPeter Zijlstra 		insn->dead_end = false;
2431e7a174fbSPeter Zijlstra 		break;
2432e7a174fbSPeter Zijlstra 
2433112765caSPeter Zijlstra 	default:
24343e7be635SJosh Poimboeuf 		ERROR_INSN(insn, "Unknown annotation type: %d", type);
2435c5995abeSJosh Poimboeuf 		return -1;
24368aa8eb2aSAlexandre Chartre 	}
24378aa8eb2aSAlexandre Chartre 
24388aa8eb2aSAlexandre Chartre 	return 0;
24398aa8eb2aSAlexandre Chartre }
24408aa8eb2aSAlexandre Chartre 
244105098119SMarco Elver /*
244205098119SMarco Elver  * Return true if name matches an instrumentation function, where calls to that
244305098119SMarco Elver  * function from noinstr code can safely be removed, but compilers won't do so.
244405098119SMarco Elver  */
is_profiling_func(const char * name)244505098119SMarco Elver static bool is_profiling_func(const char *name)
244605098119SMarco Elver {
244705098119SMarco Elver 	/*
244805098119SMarco Elver 	 * Many compilers cannot disable KCOV with a function attribute.
244905098119SMarco Elver 	 */
245005098119SMarco Elver 	if (!strncmp(name, "__sanitizer_cov_", 16))
245105098119SMarco Elver 		return true;
245205098119SMarco Elver 
245305098119SMarco Elver 	/*
245405098119SMarco Elver 	 * Some compilers currently do not remove __tsan_func_entry/exit nor
245505098119SMarco Elver 	 * __tsan_atomic_signal_fence (used for barrier instrumentation) with
245605098119SMarco Elver 	 * the __no_sanitize_thread attribute, remove them. Once the kernel's
245705098119SMarco Elver 	 * minimum Clang version is 14.0, this can be removed.
245805098119SMarco Elver 	 */
245905098119SMarco Elver 	if (!strncmp(name, "__tsan_func_", 12) ||
246005098119SMarco Elver 	    !strcmp(name, "__tsan_atomic_signal_fence"))
246105098119SMarco Elver 		return true;
246205098119SMarco Elver 
246305098119SMarco Elver 	return false;
246405098119SMarco Elver }
246505098119SMarco Elver 
classify_symbols(struct objtool_file * file)24661739c66eSPeter Zijlstra static int classify_symbols(struct objtool_file *file)
24671e7e4788SJosh Poimboeuf {
24681e7e4788SJosh Poimboeuf 	struct symbol *func;
24691e7e4788SJosh Poimboeuf 
24709290e772SJosh Poimboeuf 	for_each_sym(file, func) {
2471d5ab2bc3STiezhu Yang 		if (func->type == STT_NOTYPE && strstarts(func->name, ".L"))
2472d5ab2bc3STiezhu Yang 			func->local_label = true;
2473d5ab2bc3STiezhu Yang 
24741739c66eSPeter Zijlstra 		if (func->bind != STB_GLOBAL)
24751739c66eSPeter Zijlstra 			continue;
24761739c66eSPeter Zijlstra 
24771739c66eSPeter Zijlstra 		if (!strncmp(func->name, STATIC_CALL_TRAMP_PREFIX_STR,
24781e7e4788SJosh Poimboeuf 			     strlen(STATIC_CALL_TRAMP_PREFIX_STR)))
24791e7e4788SJosh Poimboeuf 			func->static_call_tramp = true;
24801739c66eSPeter Zijlstra 
24811739c66eSPeter Zijlstra 		if (arch_is_retpoline(func))
24821739c66eSPeter Zijlstra 			func->retpoline_thunk = true;
24831739c66eSPeter Zijlstra 
2484d9e9d230SPeter Zijlstra 		if (arch_is_rethunk(func))
2485d9e9d230SPeter Zijlstra 			func->return_thunk = true;
2486d9e9d230SPeter Zijlstra 
24874ae68b26SPeter Zijlstra 		if (arch_is_embedded_insn(func))
24884ae68b26SPeter Zijlstra 			func->embedded_insn = true;
24894ae68b26SPeter Zijlstra 
24904ca993d4SSathvika Vasireddy 		if (arch_ftrace_match(func->name))
24911739c66eSPeter Zijlstra 			func->fentry = true;
24921739c66eSPeter Zijlstra 
249305098119SMarco Elver 		if (is_profiling_func(func->name))
249405098119SMarco Elver 			func->profiling_func = true;
24951e7e4788SJosh Poimboeuf 	}
24961e7e4788SJosh Poimboeuf 
24971e7e4788SJosh Poimboeuf 	return 0;
24981e7e4788SJosh Poimboeuf }
24991e7e4788SJosh Poimboeuf 
mark_rodata(struct objtool_file * file)25004a60aa05SAllan Xavier static void mark_rodata(struct objtool_file *file)
25014a60aa05SAllan Xavier {
25024a60aa05SAllan Xavier 	struct section *sec;
25034a60aa05SAllan Xavier 	bool found = false;
25044a60aa05SAllan Xavier 
25054a60aa05SAllan Xavier 	/*
250687b512deSJosh Poimboeuf 	 * Search for the following rodata sections, each of which can
250787b512deSJosh Poimboeuf 	 * potentially contain jump tables:
250887b512deSJosh Poimboeuf 	 *
250987b512deSJosh Poimboeuf 	 * - .rodata: can contain GCC switch tables
251087b512deSJosh Poimboeuf 	 * - .rodata.<func>: same, if -fdata-sections is being used
251173cfc53cSArd Biesheuvel 	 * - .data.rel.ro.c_jump_table: contains C annotated jump tables
251287b512deSJosh Poimboeuf 	 *
251387b512deSJosh Poimboeuf 	 * .rodata.str1.* sections are ignored; they don't contain jump tables.
25144a60aa05SAllan Xavier 	 */
25154a60aa05SAllan Xavier 	for_each_sec(file, sec) {
251673cfc53cSArd Biesheuvel 		if ((!strncmp(sec->name, ".rodata", 7) &&
251773cfc53cSArd Biesheuvel 		     !strstr(sec->name, ".str1.")) ||
251873cfc53cSArd Biesheuvel 		    !strncmp(sec->name, ".data.rel.ro", 12)) {
25194a60aa05SAllan Xavier 			sec->rodata = true;
25204a60aa05SAllan Xavier 			found = true;
25214a60aa05SAllan Xavier 		}
25224a60aa05SAllan Xavier 	}
25234a60aa05SAllan Xavier 
25244a60aa05SAllan Xavier 	file->rodata = found;
25254a60aa05SAllan Xavier }
25264a60aa05SAllan Xavier 
decode_sections(struct objtool_file * file)2527dcc914f4SJosh Poimboeuf static int decode_sections(struct objtool_file *file)
2528dcc914f4SJosh Poimboeuf {
2529dcc914f4SJosh Poimboeuf 	int ret;
2530dcc914f4SJosh Poimboeuf 
25314a60aa05SAllan Xavier 	mark_rodata(file);
25324a60aa05SAllan Xavier 
2533db2b0c5dSPeter Zijlstra 	ret = init_pv_ops(file);
2534db2b0c5dSPeter Zijlstra 	if (ret)
2535db2b0c5dSPeter Zijlstra 		return ret;
2536db2b0c5dSPeter Zijlstra 
2537dbcdbdfdSPeter Zijlstra 	/*
2538dbcdbdfdSPeter Zijlstra 	 * Must be before add_{jump_call}_destination.
2539dbcdbdfdSPeter Zijlstra 	 */
2540dbcdbdfdSPeter Zijlstra 	ret = classify_symbols(file);
2541dbcdbdfdSPeter Zijlstra 	if (ret)
2542dbcdbdfdSPeter Zijlstra 		return ret;
2543dbcdbdfdSPeter Zijlstra 
2544dcc914f4SJosh Poimboeuf 	ret = decode_instructions(file);
2545dcc914f4SJosh Poimboeuf 	if (ret)
2546dcc914f4SJosh Poimboeuf 		return ret;
2547dcc914f4SJosh Poimboeuf 
2548c5995abeSJosh Poimboeuf 	ret = add_ignores(file);
2549c5995abeSJosh Poimboeuf 	if (ret)
2550c5995abeSJosh Poimboeuf 		return ret;
2551c5995abeSJosh Poimboeuf 
2552ea24213dSPeter Zijlstra 	add_uaccess_safe(file);
2553dcc914f4SJosh Poimboeuf 
2554a8a330ddSPeter Zijlstra 	ret = read_annotate(file, __annotate_early);
255596db4a98SPeter Zijlstra 	if (ret)
255696db4a98SPeter Zijlstra 		return ret;
255796db4a98SPeter Zijlstra 
2558a958c4feSPeter Zijlstra 	/*
255934c861e8SJosh Poimboeuf 	 * Must be before add_jump_destinations(), which depends on 'func'
256034c861e8SJosh Poimboeuf 	 * being set for alternatives, to enable proper sibling call detection.
256143d5430aSPeter Zijlstra 	 */
2562de6fbcedSSathvika Vasireddy 	if (opts.stackval || opts.orc || opts.uaccess || opts.noinstr) {
256334c861e8SJosh Poimboeuf 		ret = add_special_section_alts(file);
2564dcc914f4SJosh Poimboeuf 		if (ret)
2565dcc914f4SJosh Poimboeuf 			return ret;
2566de6fbcedSSathvika Vasireddy 	}
2567dcc914f4SJosh Poimboeuf 
256834c861e8SJosh Poimboeuf 	ret = add_jump_destinations(file);
2569dcc914f4SJosh Poimboeuf 	if (ret)
2570dcc914f4SJosh Poimboeuf 		return ret;
2571dcc914f4SJosh Poimboeuf 
2572a958c4feSPeter Zijlstra 	/*
2573a958c4feSPeter Zijlstra 	 * Must be before add_call_destination(); it changes INSN_CALL to
2574a958c4feSPeter Zijlstra 	 * INSN_JUMP.
2575a958c4feSPeter Zijlstra 	 */
2576112765caSPeter Zijlstra 	ret = read_annotate(file, __annotate_ifc);
25778aa8eb2aSAlexandre Chartre 	if (ret)
25788aa8eb2aSAlexandre Chartre 		return ret;
25798aa8eb2aSAlexandre Chartre 
2580a845c7cfSJosh Poimboeuf 	ret = add_call_destinations(file);
2581dcc914f4SJosh Poimboeuf 	if (ret)
2582dcc914f4SJosh Poimboeuf 		return ret;
2583dcc914f4SJosh Poimboeuf 
2584e7c2bc37SJosh Poimboeuf 	ret = add_jump_table_alts(file);
2585dcc914f4SJosh Poimboeuf 	if (ret)
2586dcc914f4SJosh Poimboeuf 		return ret;
2587dcc914f4SJosh Poimboeuf 
258839358a03SJosh Poimboeuf 	ret = read_unwind_hints(file);
258939358a03SJosh Poimboeuf 	if (ret)
259039358a03SJosh Poimboeuf 		return ret;
259139358a03SJosh Poimboeuf 
2592e7a174fbSPeter Zijlstra 	/*
2593e7a174fbSPeter Zijlstra 	 * Must be after add_call_destinations() such that it can override
2594e7a174fbSPeter Zijlstra 	 * dead_end_function() marks.
2595e7a174fbSPeter Zijlstra 	 */
2596a8a330ddSPeter Zijlstra 	ret = read_annotate(file, __annotate_late);
25974708ea14SJosh Poimboeuf 	if (ret)
25984708ea14SJosh Poimboeuf 		return ret;
25994708ea14SJosh Poimboeuf 
2600dcc914f4SJosh Poimboeuf 	return 0;
2601dcc914f4SJosh Poimboeuf }
2602dcc914f4SJosh Poimboeuf 
is_special_call(struct instruction * insn)2603dbf46008SPeter Zijlstra static bool is_special_call(struct instruction *insn)
2604dcc914f4SJosh Poimboeuf {
2605dbf46008SPeter Zijlstra 	if (insn->type == INSN_CALL) {
2606dbf46008SPeter Zijlstra 		struct symbol *dest = insn_call_dest(insn);
2607dbf46008SPeter Zijlstra 
2608dbf46008SPeter Zijlstra 		if (!dest)
2609dbf46008SPeter Zijlstra 			return false;
2610dbf46008SPeter Zijlstra 
2611dbf46008SPeter Zijlstra 		if (dest->fentry || dest->embedded_insn)
2612dcc914f4SJosh Poimboeuf 			return true;
2613dbf46008SPeter Zijlstra 	}
2614dcc914f4SJosh Poimboeuf 
2615dcc914f4SJosh Poimboeuf 	return false;
2616dcc914f4SJosh Poimboeuf }
2617dcc914f4SJosh Poimboeuf 
has_modified_stack_frame(struct instruction * insn,struct insn_state * state)2618e25eea89SPeter Zijlstra static bool has_modified_stack_frame(struct instruction *insn, struct insn_state *state)
2619dcc914f4SJosh Poimboeuf {
2620e7c0219bSPeter Zijlstra 	struct cfi_state *cfi = &state->cfi;
2621baa41469SJosh Poimboeuf 	int i;
2622baa41469SJosh Poimboeuf 
2623e7c0219bSPeter Zijlstra 	if (cfi->cfa.base != initial_func_cfi.cfa.base || cfi->drap)
2624baa41469SJosh Poimboeuf 		return true;
2625baa41469SJosh Poimboeuf 
2626b735bd3eSJosh Poimboeuf 	if (cfi->cfa.offset != initial_func_cfi.cfa.offset)
2627e25eea89SPeter Zijlstra 		return true;
2628e25eea89SPeter Zijlstra 
2629b735bd3eSJosh Poimboeuf 	if (cfi->stack_size != initial_func_cfi.cfa.offset)
2630e25eea89SPeter Zijlstra 		return true;
2631e25eea89SPeter Zijlstra 
2632e25eea89SPeter Zijlstra 	for (i = 0; i < CFI_NUM_REGS; i++) {
2633e7c0219bSPeter Zijlstra 		if (cfi->regs[i].base != initial_func_cfi.regs[i].base ||
2634e7c0219bSPeter Zijlstra 		    cfi->regs[i].offset != initial_func_cfi.regs[i].offset)
2635baa41469SJosh Poimboeuf 			return true;
2636e25eea89SPeter Zijlstra 	}
2637baa41469SJosh Poimboeuf 
2638baa41469SJosh Poimboeuf 	return false;
2639dcc914f4SJosh Poimboeuf }
2640dcc914f4SJosh Poimboeuf 
check_reg_frame_pos(const struct cfi_reg * reg,int expected_offset)2641fb084fdeSJulien Thierry static bool check_reg_frame_pos(const struct cfi_reg *reg,
2642fb084fdeSJulien Thierry 				int expected_offset)
2643fb084fdeSJulien Thierry {
2644fb084fdeSJulien Thierry 	return reg->base == CFI_CFA &&
2645fb084fdeSJulien Thierry 	       reg->offset == expected_offset;
2646fb084fdeSJulien Thierry }
2647fb084fdeSJulien Thierry 
has_valid_stack_frame(struct insn_state * state)2648baa41469SJosh Poimboeuf static bool has_valid_stack_frame(struct insn_state *state)
2649dcc914f4SJosh Poimboeuf {
2650e7c0219bSPeter Zijlstra 	struct cfi_state *cfi = &state->cfi;
2651e7c0219bSPeter Zijlstra 
2652fb084fdeSJulien Thierry 	if (cfi->cfa.base == CFI_BP &&
2653fb084fdeSJulien Thierry 	    check_reg_frame_pos(&cfi->regs[CFI_BP], -cfi->cfa.offset) &&
2654fb084fdeSJulien Thierry 	    check_reg_frame_pos(&cfi->regs[CFI_RA], -cfi->cfa.offset + 8))
2655baa41469SJosh Poimboeuf 		return true;
2656baa41469SJosh Poimboeuf 
2657e7c0219bSPeter Zijlstra 	if (cfi->drap && cfi->regs[CFI_BP].base == CFI_BP)
2658baa41469SJosh Poimboeuf 		return true;
2659baa41469SJosh Poimboeuf 
2660baa41469SJosh Poimboeuf 	return false;
2661dcc914f4SJosh Poimboeuf }
2662dcc914f4SJosh Poimboeuf 
update_cfi_state_regs(struct instruction * insn,struct cfi_state * cfi,struct stack_op * op)2663e7c0219bSPeter Zijlstra static int update_cfi_state_regs(struct instruction *insn,
2664e7c0219bSPeter Zijlstra 				  struct cfi_state *cfi,
266565ea47dcSJulien Thierry 				  struct stack_op *op)
2666627fce14SJosh Poimboeuf {
2667e7c0219bSPeter Zijlstra 	struct cfi_reg *cfa = &cfi->cfa;
2668627fce14SJosh Poimboeuf 
2669d8dd25a4SJosh Poimboeuf 	if (cfa->base != CFI_SP && cfa->base != CFI_SP_INDIRECT)
2670627fce14SJosh Poimboeuf 		return 0;
2671627fce14SJosh Poimboeuf 
2672627fce14SJosh Poimboeuf 	/* push */
2673ea24213dSPeter Zijlstra 	if (op->dest.type == OP_DEST_PUSH || op->dest.type == OP_DEST_PUSHF)
2674627fce14SJosh Poimboeuf 		cfa->offset += 8;
2675627fce14SJosh Poimboeuf 
2676627fce14SJosh Poimboeuf 	/* pop */
2677ea24213dSPeter Zijlstra 	if (op->src.type == OP_SRC_POP || op->src.type == OP_SRC_POPF)
2678627fce14SJosh Poimboeuf 		cfa->offset -= 8;
2679627fce14SJosh Poimboeuf 
2680627fce14SJosh Poimboeuf 	/* add immediate to sp */
2681627fce14SJosh Poimboeuf 	if (op->dest.type == OP_DEST_REG && op->src.type == OP_SRC_ADD &&
2682627fce14SJosh Poimboeuf 	    op->dest.reg == CFI_SP && op->src.reg == CFI_SP)
2683627fce14SJosh Poimboeuf 		cfa->offset -= op->src.offset;
2684627fce14SJosh Poimboeuf 
2685627fce14SJosh Poimboeuf 	return 0;
2686627fce14SJosh Poimboeuf }
2687627fce14SJosh Poimboeuf 
save_reg(struct cfi_state * cfi,unsigned char reg,int base,int offset)2688e7c0219bSPeter Zijlstra static void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int offset)
2689dcc914f4SJosh Poimboeuf {
2690bf4d1a83SJosh Poimboeuf 	if (arch_callee_saved_reg(reg) &&
2691e7c0219bSPeter Zijlstra 	    cfi->regs[reg].base == CFI_UNDEFINED) {
2692e7c0219bSPeter Zijlstra 		cfi->regs[reg].base = base;
2693e7c0219bSPeter Zijlstra 		cfi->regs[reg].offset = offset;
2694baa41469SJosh Poimboeuf 	}
2695baa41469SJosh Poimboeuf }
2696baa41469SJosh Poimboeuf 
restore_reg(struct cfi_state * cfi,unsigned char reg)2697e7c0219bSPeter Zijlstra static void restore_reg(struct cfi_state *cfi, unsigned char reg)
2698baa41469SJosh Poimboeuf {
2699e7c0219bSPeter Zijlstra 	cfi->regs[reg].base = initial_func_cfi.regs[reg].base;
2700e7c0219bSPeter Zijlstra 	cfi->regs[reg].offset = initial_func_cfi.regs[reg].offset;
2701baa41469SJosh Poimboeuf }
2702baa41469SJosh Poimboeuf 
2703baa41469SJosh Poimboeuf /*
2704baa41469SJosh Poimboeuf  * A note about DRAP stack alignment:
2705baa41469SJosh Poimboeuf  *
2706baa41469SJosh Poimboeuf  * GCC has the concept of a DRAP register, which is used to help keep track of
2707baa41469SJosh Poimboeuf  * the stack pointer when aligning the stack.  r10 or r13 is used as the DRAP
2708baa41469SJosh Poimboeuf  * register.  The typical DRAP pattern is:
2709baa41469SJosh Poimboeuf  *
2710baa41469SJosh Poimboeuf  *   4c 8d 54 24 08		lea    0x8(%rsp),%r10
2711baa41469SJosh Poimboeuf  *   48 83 e4 c0		and    $0xffffffffffffffc0,%rsp
2712baa41469SJosh Poimboeuf  *   41 ff 72 f8		pushq  -0x8(%r10)
2713baa41469SJosh Poimboeuf  *   55				push   %rbp
2714baa41469SJosh Poimboeuf  *   48 89 e5			mov    %rsp,%rbp
2715baa41469SJosh Poimboeuf  *				(more pushes)
2716baa41469SJosh Poimboeuf  *   41 52			push   %r10
2717baa41469SJosh Poimboeuf  *				...
2718baa41469SJosh Poimboeuf  *   41 5a			pop    %r10
2719baa41469SJosh Poimboeuf  *				(more pops)
2720baa41469SJosh Poimboeuf  *   5d				pop    %rbp
2721baa41469SJosh Poimboeuf  *   49 8d 62 f8		lea    -0x8(%r10),%rsp
2722baa41469SJosh Poimboeuf  *   c3				retq
2723baa41469SJosh Poimboeuf  *
2724baa41469SJosh Poimboeuf  * There are some variations in the epilogues, like:
2725baa41469SJosh Poimboeuf  *
2726baa41469SJosh Poimboeuf  *   5b				pop    %rbx
2727baa41469SJosh Poimboeuf  *   41 5a			pop    %r10
2728baa41469SJosh Poimboeuf  *   41 5c			pop    %r12
2729baa41469SJosh Poimboeuf  *   41 5d			pop    %r13
2730baa41469SJosh Poimboeuf  *   41 5e			pop    %r14
2731baa41469SJosh Poimboeuf  *   c9				leaveq
2732baa41469SJosh Poimboeuf  *   49 8d 62 f8		lea    -0x8(%r10),%rsp
2733baa41469SJosh Poimboeuf  *   c3				retq
2734baa41469SJosh Poimboeuf  *
2735baa41469SJosh Poimboeuf  * and:
2736baa41469SJosh Poimboeuf  *
2737baa41469SJosh Poimboeuf  *   4c 8b 55 e8		mov    -0x18(%rbp),%r10
2738baa41469SJosh Poimboeuf  *   48 8b 5d e0		mov    -0x20(%rbp),%rbx
2739baa41469SJosh Poimboeuf  *   4c 8b 65 f0		mov    -0x10(%rbp),%r12
2740baa41469SJosh Poimboeuf  *   4c 8b 6d f8		mov    -0x8(%rbp),%r13
2741baa41469SJosh Poimboeuf  *   c9				leaveq
2742baa41469SJosh Poimboeuf  *   49 8d 62 f8		lea    -0x8(%r10),%rsp
2743baa41469SJosh Poimboeuf  *   c3				retq
2744baa41469SJosh Poimboeuf  *
2745baa41469SJosh Poimboeuf  * Sometimes r13 is used as the DRAP register, in which case it's saved and
2746baa41469SJosh Poimboeuf  * restored beforehand:
2747baa41469SJosh Poimboeuf  *
2748baa41469SJosh Poimboeuf  *   41 55			push   %r13
2749baa41469SJosh Poimboeuf  *   4c 8d 6c 24 10		lea    0x10(%rsp),%r13
2750baa41469SJosh Poimboeuf  *   48 83 e4 f0		and    $0xfffffffffffffff0,%rsp
2751baa41469SJosh Poimboeuf  *				...
2752baa41469SJosh Poimboeuf  *   49 8d 65 f0		lea    -0x10(%r13),%rsp
2753baa41469SJosh Poimboeuf  *   41 5d			pop    %r13
2754baa41469SJosh Poimboeuf  *   c3				retq
2755baa41469SJosh Poimboeuf  */
update_cfi_state(struct instruction * insn,struct instruction * next_insn,struct cfi_state * cfi,struct stack_op * op)2756d54dba41SPeter Zijlstra static int update_cfi_state(struct instruction *insn,
2757d54dba41SPeter Zijlstra 			    struct instruction *next_insn,
2758d54dba41SPeter Zijlstra 			    struct cfi_state *cfi, struct stack_op *op)
2759baa41469SJosh Poimboeuf {
2760e7c0219bSPeter Zijlstra 	struct cfi_reg *cfa = &cfi->cfa;
2761e7c0219bSPeter Zijlstra 	struct cfi_reg *regs = cfi->regs;
2762baa41469SJosh Poimboeuf 
27631e4b6191SJosh Poimboeuf 	/* ignore UNWIND_HINT_UNDEFINED regions */
27641e4b6191SJosh Poimboeuf 	if (cfi->force_undefined)
27651e4b6191SJosh Poimboeuf 		return 0;
27661e4b6191SJosh Poimboeuf 
2767baa41469SJosh Poimboeuf 	/* stack operations don't make sense with an undefined CFA */
2768baa41469SJosh Poimboeuf 	if (cfa->base == CFI_UNDEFINED) {
2769dbcdbdfdSPeter Zijlstra 		if (insn_func(insn)) {
2770246b2c85SJosh Poimboeuf 			WARN_INSN(insn, "undefined stack state");
2771c5995abeSJosh Poimboeuf 			return 1;
2772baa41469SJosh Poimboeuf 		}
2773baa41469SJosh Poimboeuf 		return 0;
2774baa41469SJosh Poimboeuf 	}
2775baa41469SJosh Poimboeuf 
2776ee819aedSJulien Thierry 	if (cfi->type == UNWIND_HINT_TYPE_REGS ||
2777ee819aedSJulien Thierry 	    cfi->type == UNWIND_HINT_TYPE_REGS_PARTIAL)
2778e7c0219bSPeter Zijlstra 		return update_cfi_state_regs(insn, cfi, op);
2779627fce14SJosh Poimboeuf 
2780baa41469SJosh Poimboeuf 	switch (op->dest.type) {
2781baa41469SJosh Poimboeuf 
2782baa41469SJosh Poimboeuf 	case OP_DEST_REG:
2783baa41469SJosh Poimboeuf 		switch (op->src.type) {
2784baa41469SJosh Poimboeuf 
2785baa41469SJosh Poimboeuf 		case OP_SRC_REG:
27860d0970eeSJosh Poimboeuf 			if (op->src.reg == CFI_SP && op->dest.reg == CFI_BP &&
27870d0970eeSJosh Poimboeuf 			    cfa->base == CFI_SP &&
2788fb084fdeSJulien Thierry 			    check_reg_frame_pos(&regs[CFI_BP], -cfa->offset)) {
2789baa41469SJosh Poimboeuf 
2790baa41469SJosh Poimboeuf 				/* mov %rsp, %rbp */
2791baa41469SJosh Poimboeuf 				cfa->base = op->dest.reg;
2792e7c0219bSPeter Zijlstra 				cfi->bp_scratch = false;
2793dd88a0a0SJosh Poimboeuf 			}
2794dd88a0a0SJosh Poimboeuf 
27950d0970eeSJosh Poimboeuf 			else if (op->src.reg == CFI_SP &&
2796e7c0219bSPeter Zijlstra 				 op->dest.reg == CFI_BP && cfi->drap) {
2797baa41469SJosh Poimboeuf 
2798baa41469SJosh Poimboeuf 				/* drap: mov %rsp, %rbp */
2799baa41469SJosh Poimboeuf 				regs[CFI_BP].base = CFI_BP;
2800e7c0219bSPeter Zijlstra 				regs[CFI_BP].offset = -cfi->stack_size;
2801e7c0219bSPeter Zijlstra 				cfi->bp_scratch = false;
2802dd88a0a0SJosh Poimboeuf 			}
28030d0970eeSJosh Poimboeuf 
28040d0970eeSJosh Poimboeuf 			else if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
28050d0970eeSJosh Poimboeuf 
28060d0970eeSJosh Poimboeuf 				/*
28070d0970eeSJosh Poimboeuf 				 * mov %rsp, %reg
28080d0970eeSJosh Poimboeuf 				 *
28090d0970eeSJosh Poimboeuf 				 * This is needed for the rare case where GCC
28100d0970eeSJosh Poimboeuf 				 * does:
28110d0970eeSJosh Poimboeuf 				 *
28120d0970eeSJosh Poimboeuf 				 *   mov    %rsp, %rax
28130d0970eeSJosh Poimboeuf 				 *   ...
28140d0970eeSJosh Poimboeuf 				 *   mov    %rax, %rsp
28150d0970eeSJosh Poimboeuf 				 */
2816e7c0219bSPeter Zijlstra 				cfi->vals[op->dest.reg].base = CFI_CFA;
2817e7c0219bSPeter Zijlstra 				cfi->vals[op->dest.reg].offset = -cfi->stack_size;
2818dd88a0a0SJosh Poimboeuf 			}
2819baa41469SJosh Poimboeuf 
28203c1f0583SJosh Poimboeuf 			else if (op->src.reg == CFI_BP && op->dest.reg == CFI_SP &&
2821ffc7e74fSPeter Zijlstra 				 (cfa->base == CFI_BP || cfa->base == cfi->drap_reg)) {
28223c1f0583SJosh Poimboeuf 
28233c1f0583SJosh Poimboeuf 				/*
28243c1f0583SJosh Poimboeuf 				 * mov %rbp, %rsp
28253c1f0583SJosh Poimboeuf 				 *
28263c1f0583SJosh Poimboeuf 				 * Restore the original stack pointer (Clang).
28273c1f0583SJosh Poimboeuf 				 */
2828e7c0219bSPeter Zijlstra 				cfi->stack_size = -cfi->regs[CFI_BP].offset;
28293c1f0583SJosh Poimboeuf 			}
28303c1f0583SJosh Poimboeuf 
2831dd88a0a0SJosh Poimboeuf 			else if (op->dest.reg == cfa->base) {
2832dd88a0a0SJosh Poimboeuf 
2833dd88a0a0SJosh Poimboeuf 				/* mov %reg, %rsp */
2834dd88a0a0SJosh Poimboeuf 				if (cfa->base == CFI_SP &&
2835e7c0219bSPeter Zijlstra 				    cfi->vals[op->src.reg].base == CFI_CFA) {
2836dd88a0a0SJosh Poimboeuf 
2837dd88a0a0SJosh Poimboeuf 					/*
2838dd88a0a0SJosh Poimboeuf 					 * This is needed for the rare case
2839dd88a0a0SJosh Poimboeuf 					 * where GCC does something dumb like:
2840dd88a0a0SJosh Poimboeuf 					 *
2841dd88a0a0SJosh Poimboeuf 					 *   lea    0x8(%rsp), %rcx
2842dd88a0a0SJosh Poimboeuf 					 *   ...
2843dd88a0a0SJosh Poimboeuf 					 *   mov    %rcx, %rsp
2844dd88a0a0SJosh Poimboeuf 					 */
2845e7c0219bSPeter Zijlstra 					cfa->offset = -cfi->vals[op->src.reg].offset;
2846e7c0219bSPeter Zijlstra 					cfi->stack_size = cfa->offset;
2847dd88a0a0SJosh Poimboeuf 
2848aafeb14eSPeter Zijlstra 				} else if (cfa->base == CFI_SP &&
2849aafeb14eSPeter Zijlstra 					   cfi->vals[op->src.reg].base == CFI_SP_INDIRECT &&
2850aafeb14eSPeter Zijlstra 					   cfi->vals[op->src.reg].offset == cfa->offset) {
2851aafeb14eSPeter Zijlstra 
2852aafeb14eSPeter Zijlstra 					/*
2853aafeb14eSPeter Zijlstra 					 * Stack swizzle:
2854aafeb14eSPeter Zijlstra 					 *
2855aafeb14eSPeter Zijlstra 					 * 1: mov %rsp, (%[tos])
2856aafeb14eSPeter Zijlstra 					 * 2: mov %[tos], %rsp
2857aafeb14eSPeter Zijlstra 					 *    ...
2858aafeb14eSPeter Zijlstra 					 * 3: pop %rsp
2859aafeb14eSPeter Zijlstra 					 *
2860aafeb14eSPeter Zijlstra 					 * Where:
2861aafeb14eSPeter Zijlstra 					 *
2862aafeb14eSPeter Zijlstra 					 * 1 - places a pointer to the previous
2863aafeb14eSPeter Zijlstra 					 *     stack at the Top-of-Stack of the
2864aafeb14eSPeter Zijlstra 					 *     new stack.
2865aafeb14eSPeter Zijlstra 					 *
2866aafeb14eSPeter Zijlstra 					 * 2 - switches to the new stack.
2867aafeb14eSPeter Zijlstra 					 *
2868aafeb14eSPeter Zijlstra 					 * 3 - pops the Top-of-Stack to restore
2869aafeb14eSPeter Zijlstra 					 *     the original stack.
2870aafeb14eSPeter Zijlstra 					 *
2871aafeb14eSPeter Zijlstra 					 * Note: we set base to SP_INDIRECT
2872aafeb14eSPeter Zijlstra 					 * here and preserve offset. Therefore
2873aafeb14eSPeter Zijlstra 					 * when the unwinder reaches ToS it
2874aafeb14eSPeter Zijlstra 					 * will dereference SP and then add the
2875aafeb14eSPeter Zijlstra 					 * offset to find the next frame, IOW:
2876aafeb14eSPeter Zijlstra 					 * (%rsp) + offset.
2877aafeb14eSPeter Zijlstra 					 */
2878aafeb14eSPeter Zijlstra 					cfa->base = CFI_SP_INDIRECT;
2879aafeb14eSPeter Zijlstra 
2880dd88a0a0SJosh Poimboeuf 				} else {
2881dd88a0a0SJosh Poimboeuf 					cfa->base = CFI_UNDEFINED;
2882dd88a0a0SJosh Poimboeuf 					cfa->offset = 0;
2883dd88a0a0SJosh Poimboeuf 				}
2884baa41469SJosh Poimboeuf 			}
2885baa41469SJosh Poimboeuf 
2886724c8a23SPeter Zijlstra 			else if (op->dest.reg == CFI_SP &&
2887724c8a23SPeter Zijlstra 				 cfi->vals[op->src.reg].base == CFI_SP_INDIRECT &&
2888724c8a23SPeter Zijlstra 				 cfi->vals[op->src.reg].offset == cfa->offset) {
2889724c8a23SPeter Zijlstra 
2890724c8a23SPeter Zijlstra 				/*
2891724c8a23SPeter Zijlstra 				 * The same stack swizzle case 2) as above. But
2892724c8a23SPeter Zijlstra 				 * because we can't change cfa->base, case 3)
2893724c8a23SPeter Zijlstra 				 * will become a regular POP. Pretend we're a
2894724c8a23SPeter Zijlstra 				 * PUSH so things don't go unbalanced.
2895724c8a23SPeter Zijlstra 				 */
2896724c8a23SPeter Zijlstra 				cfi->stack_size += 8;
2897724c8a23SPeter Zijlstra 			}
2898724c8a23SPeter Zijlstra 
2899724c8a23SPeter Zijlstra 
2900baa41469SJosh Poimboeuf 			break;
2901baa41469SJosh Poimboeuf 
2902baa41469SJosh Poimboeuf 		case OP_SRC_ADD:
2903baa41469SJosh Poimboeuf 			if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) {
2904baa41469SJosh Poimboeuf 
2905baa41469SJosh Poimboeuf 				/* add imm, %rsp */
2906e7c0219bSPeter Zijlstra 				cfi->stack_size -= op->src.offset;
2907baa41469SJosh Poimboeuf 				if (cfa->base == CFI_SP)
2908baa41469SJosh Poimboeuf 					cfa->offset -= op->src.offset;
2909baa41469SJosh Poimboeuf 				break;
2910baa41469SJosh Poimboeuf 			}
2911baa41469SJosh Poimboeuf 
2912da5b2ad1STiezhu Yang 			if (op->dest.reg == CFI_BP && op->src.reg == CFI_SP &&
2913da5b2ad1STiezhu Yang 			    insn->sym->frame_pointer) {
2914da5b2ad1STiezhu Yang 				/* addi.d fp,sp,imm on LoongArch */
2915da5b2ad1STiezhu Yang 				if (cfa->base == CFI_SP && cfa->offset == op->src.offset) {
2916da5b2ad1STiezhu Yang 					cfa->base = CFI_BP;
2917da5b2ad1STiezhu Yang 					cfa->offset = 0;
2918da5b2ad1STiezhu Yang 				}
2919da5b2ad1STiezhu Yang 				break;
2920da5b2ad1STiezhu Yang 			}
2921baa41469SJosh Poimboeuf 
2922da5b2ad1STiezhu Yang 			if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) {
2923da5b2ad1STiezhu Yang 				/* addi.d sp,fp,imm on LoongArch */
2924da5b2ad1STiezhu Yang 				if (cfa->base == CFI_BP && cfa->offset == 0) {
2925da5b2ad1STiezhu Yang 					if (insn->sym->frame_pointer) {
2926da5b2ad1STiezhu Yang 						cfa->base = CFI_SP;
2927da5b2ad1STiezhu Yang 						cfa->offset = -op->src.offset;
2928da5b2ad1STiezhu Yang 					}
2929da5b2ad1STiezhu Yang 				} else {
2930baa41469SJosh Poimboeuf 					/* lea disp(%rbp), %rsp */
2931e7c0219bSPeter Zijlstra 					cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset);
2932da5b2ad1STiezhu Yang 				}
2933baa41469SJosh Poimboeuf 				break;
2934baa41469SJosh Poimboeuf 			}
2935baa41469SJosh Poimboeuf 
2936dd88a0a0SJosh Poimboeuf 			if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
2937baa41469SJosh Poimboeuf 
2938baa41469SJosh Poimboeuf 				/* drap: lea disp(%rsp), %drap */
2939e7c0219bSPeter Zijlstra 				cfi->drap_reg = op->dest.reg;
2940dd88a0a0SJosh Poimboeuf 
2941dd88a0a0SJosh Poimboeuf 				/*
2942dd88a0a0SJosh Poimboeuf 				 * lea disp(%rsp), %reg
2943dd88a0a0SJosh Poimboeuf 				 *
2944dd88a0a0SJosh Poimboeuf 				 * This is needed for the rare case where GCC
2945dd88a0a0SJosh Poimboeuf 				 * does something dumb like:
2946dd88a0a0SJosh Poimboeuf 				 *
2947dd88a0a0SJosh Poimboeuf 				 *   lea    0x8(%rsp), %rcx
2948dd88a0a0SJosh Poimboeuf 				 *   ...
2949dd88a0a0SJosh Poimboeuf 				 *   mov    %rcx, %rsp
2950dd88a0a0SJosh Poimboeuf 				 */
2951e7c0219bSPeter Zijlstra 				cfi->vals[op->dest.reg].base = CFI_CFA;
2952e7c0219bSPeter Zijlstra 				cfi->vals[op->dest.reg].offset = \
2953e7c0219bSPeter Zijlstra 					-cfi->stack_size + op->src.offset;
2954dd88a0a0SJosh Poimboeuf 
2955baa41469SJosh Poimboeuf 				break;
2956baa41469SJosh Poimboeuf 			}
2957baa41469SJosh Poimboeuf 
2958e7c0219bSPeter Zijlstra 			if (cfi->drap && op->dest.reg == CFI_SP &&
2959e7c0219bSPeter Zijlstra 			    op->src.reg == cfi->drap_reg) {
2960baa41469SJosh Poimboeuf 
2961baa41469SJosh Poimboeuf 				 /* drap: lea disp(%drap), %rsp */
2962baa41469SJosh Poimboeuf 				cfa->base = CFI_SP;
2963e7c0219bSPeter Zijlstra 				cfa->offset = cfi->stack_size = -op->src.offset;
2964e7c0219bSPeter Zijlstra 				cfi->drap_reg = CFI_UNDEFINED;
2965e7c0219bSPeter Zijlstra 				cfi->drap = false;
2966baa41469SJosh Poimboeuf 				break;
2967baa41469SJosh Poimboeuf 			}
2968baa41469SJosh Poimboeuf 
2969d54dba41SPeter Zijlstra 			if (op->dest.reg == cfi->cfa.base && !(next_insn && next_insn->hint)) {
2970246b2c85SJosh Poimboeuf 				WARN_INSN(insn, "unsupported stack register modification");
2971baa41469SJosh Poimboeuf 				return -1;
2972baa41469SJosh Poimboeuf 			}
2973baa41469SJosh Poimboeuf 
2974baa41469SJosh Poimboeuf 			break;
2975baa41469SJosh Poimboeuf 
2976baa41469SJosh Poimboeuf 		case OP_SRC_AND:
2977baa41469SJosh Poimboeuf 			if (op->dest.reg != CFI_SP ||
2978e7c0219bSPeter Zijlstra 			    (cfi->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
2979e7c0219bSPeter Zijlstra 			    (cfi->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
2980246b2c85SJosh Poimboeuf 				WARN_INSN(insn, "unsupported stack pointer realignment");
2981baa41469SJosh Poimboeuf 				return -1;
2982baa41469SJosh Poimboeuf 			}
2983baa41469SJosh Poimboeuf 
2984e7c0219bSPeter Zijlstra 			if (cfi->drap_reg != CFI_UNDEFINED) {
2985baa41469SJosh Poimboeuf 				/* drap: and imm, %rsp */
2986e7c0219bSPeter Zijlstra 				cfa->base = cfi->drap_reg;
2987e7c0219bSPeter Zijlstra 				cfa->offset = cfi->stack_size = 0;
2988e7c0219bSPeter Zijlstra 				cfi->drap = true;
2989baa41469SJosh Poimboeuf 			}
2990baa41469SJosh Poimboeuf 
2991baa41469SJosh Poimboeuf 			/*
2992baa41469SJosh Poimboeuf 			 * Older versions of GCC (4.8ish) realign the stack
2993baa41469SJosh Poimboeuf 			 * without DRAP, with a frame pointer.
2994baa41469SJosh Poimboeuf 			 */
2995baa41469SJosh Poimboeuf 
2996baa41469SJosh Poimboeuf 			break;
2997baa41469SJosh Poimboeuf 
2998baa41469SJosh Poimboeuf 		case OP_SRC_POP:
2999ea24213dSPeter Zijlstra 		case OP_SRC_POPF:
3000aafeb14eSPeter Zijlstra 			if (op->dest.reg == CFI_SP && cfa->base == CFI_SP_INDIRECT) {
3001aafeb14eSPeter Zijlstra 
3002aafeb14eSPeter Zijlstra 				/* pop %rsp; # restore from a stack swizzle */
3003aafeb14eSPeter Zijlstra 				cfa->base = CFI_SP;
3004aafeb14eSPeter Zijlstra 				break;
3005aafeb14eSPeter Zijlstra 			}
3006aafeb14eSPeter Zijlstra 
3007e7c0219bSPeter Zijlstra 			if (!cfi->drap && op->dest.reg == cfa->base) {
3008baa41469SJosh Poimboeuf 
3009baa41469SJosh Poimboeuf 				/* pop %rbp */
3010baa41469SJosh Poimboeuf 				cfa->base = CFI_SP;
3011baa41469SJosh Poimboeuf 			}
3012baa41469SJosh Poimboeuf 
3013e7c0219bSPeter Zijlstra 			if (cfi->drap && cfa->base == CFI_BP_INDIRECT &&
3014e7c0219bSPeter Zijlstra 			    op->dest.reg == cfi->drap_reg &&
3015e7c0219bSPeter Zijlstra 			    cfi->drap_offset == -cfi->stack_size) {
3016baa41469SJosh Poimboeuf 
3017baa41469SJosh Poimboeuf 				/* drap: pop %drap */
3018e7c0219bSPeter Zijlstra 				cfa->base = cfi->drap_reg;
3019baa41469SJosh Poimboeuf 				cfa->offset = 0;
3020e7c0219bSPeter Zijlstra 				cfi->drap_offset = -1;
3021baa41469SJosh Poimboeuf 
3022ffc7e74fSPeter Zijlstra 			} else if (cfi->stack_size == -regs[op->dest.reg].offset) {
3023bf4d1a83SJosh Poimboeuf 
3024bf4d1a83SJosh Poimboeuf 				/* pop %reg */
3025e7c0219bSPeter Zijlstra 				restore_reg(cfi, op->dest.reg);
3026baa41469SJosh Poimboeuf 			}
3027baa41469SJosh Poimboeuf 
3028e7c0219bSPeter Zijlstra 			cfi->stack_size -= 8;
3029baa41469SJosh Poimboeuf 			if (cfa->base == CFI_SP)
3030baa41469SJosh Poimboeuf 				cfa->offset -= 8;
3031baa41469SJosh Poimboeuf 
3032baa41469SJosh Poimboeuf 			break;
3033baa41469SJosh Poimboeuf 
3034baa41469SJosh Poimboeuf 		case OP_SRC_REG_INDIRECT:
3035201ef5a9SJulien Thierry 			if (!cfi->drap && op->dest.reg == cfa->base &&
3036201ef5a9SJulien Thierry 			    op->dest.reg == CFI_BP) {
3037201ef5a9SJulien Thierry 
3038201ef5a9SJulien Thierry 				/* mov disp(%rsp), %rbp */
3039201ef5a9SJulien Thierry 				cfa->base = CFI_SP;
3040201ef5a9SJulien Thierry 				cfa->offset = cfi->stack_size;
3041201ef5a9SJulien Thierry 			}
3042201ef5a9SJulien Thierry 
3043e7c0219bSPeter Zijlstra 			if (cfi->drap && op->src.reg == CFI_BP &&
3044e7c0219bSPeter Zijlstra 			    op->src.offset == cfi->drap_offset) {
3045bf4d1a83SJosh Poimboeuf 
3046bf4d1a83SJosh Poimboeuf 				/* drap: mov disp(%rbp), %drap */
3047e7c0219bSPeter Zijlstra 				cfa->base = cfi->drap_reg;
3048bf4d1a83SJosh Poimboeuf 				cfa->offset = 0;
3049e7c0219bSPeter Zijlstra 				cfi->drap_offset = -1;
3050bf4d1a83SJosh Poimboeuf 			}
3051bf4d1a83SJosh Poimboeuf 
3052e7c0219bSPeter Zijlstra 			if (cfi->drap && op->src.reg == CFI_BP &&
3053baa41469SJosh Poimboeuf 			    op->src.offset == regs[op->dest.reg].offset) {
3054baa41469SJosh Poimboeuf 
3055baa41469SJosh Poimboeuf 				/* drap: mov disp(%rbp), %reg */
3056e7c0219bSPeter Zijlstra 				restore_reg(cfi, op->dest.reg);
3057baa41469SJosh Poimboeuf 
3058baa41469SJosh Poimboeuf 			} else if (op->src.reg == cfa->base &&
3059baa41469SJosh Poimboeuf 			    op->src.offset == regs[op->dest.reg].offset + cfa->offset) {
3060baa41469SJosh Poimboeuf 
3061baa41469SJosh Poimboeuf 				/* mov disp(%rbp), %reg */
3062baa41469SJosh Poimboeuf 				/* mov disp(%rsp), %reg */
3063e7c0219bSPeter Zijlstra 				restore_reg(cfi, op->dest.reg);
3064201ef5a9SJulien Thierry 
3065201ef5a9SJulien Thierry 			} else if (op->src.reg == CFI_SP &&
3066201ef5a9SJulien Thierry 				   op->src.offset == regs[op->dest.reg].offset + cfi->stack_size) {
3067201ef5a9SJulien Thierry 
3068201ef5a9SJulien Thierry 				/* mov disp(%rsp), %reg */
3069201ef5a9SJulien Thierry 				restore_reg(cfi, op->dest.reg);
3070baa41469SJosh Poimboeuf 			}
3071baa41469SJosh Poimboeuf 
3072baa41469SJosh Poimboeuf 			break;
3073baa41469SJosh Poimboeuf 
3074baa41469SJosh Poimboeuf 		default:
3075246b2c85SJosh Poimboeuf 			WARN_INSN(insn, "unknown stack-related instruction");
3076baa41469SJosh Poimboeuf 			return -1;
3077baa41469SJosh Poimboeuf 		}
3078baa41469SJosh Poimboeuf 
3079baa41469SJosh Poimboeuf 		break;
3080baa41469SJosh Poimboeuf 
3081baa41469SJosh Poimboeuf 	case OP_DEST_PUSH:
3082ea24213dSPeter Zijlstra 	case OP_DEST_PUSHF:
3083e7c0219bSPeter Zijlstra 		cfi->stack_size += 8;
3084baa41469SJosh Poimboeuf 		if (cfa->base == CFI_SP)
3085baa41469SJosh Poimboeuf 			cfa->offset += 8;
3086baa41469SJosh Poimboeuf 
3087baa41469SJosh Poimboeuf 		if (op->src.type != OP_SRC_REG)
3088baa41469SJosh Poimboeuf 			break;
3089baa41469SJosh Poimboeuf 
3090e7c0219bSPeter Zijlstra 		if (cfi->drap) {
3091e7c0219bSPeter Zijlstra 			if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
3092baa41469SJosh Poimboeuf 
3093baa41469SJosh Poimboeuf 				/* drap: push %drap */
3094baa41469SJosh Poimboeuf 				cfa->base = CFI_BP_INDIRECT;
3095e7c0219bSPeter Zijlstra 				cfa->offset = -cfi->stack_size;
3096baa41469SJosh Poimboeuf 
3097bf4d1a83SJosh Poimboeuf 				/* save drap so we know when to restore it */
3098e7c0219bSPeter Zijlstra 				cfi->drap_offset = -cfi->stack_size;
3099baa41469SJosh Poimboeuf 
3100e7c0219bSPeter Zijlstra 			} else if (op->src.reg == CFI_BP && cfa->base == cfi->drap_reg) {
3101baa41469SJosh Poimboeuf 
3102baa41469SJosh Poimboeuf 				/* drap: push %rbp */
3103e7c0219bSPeter Zijlstra 				cfi->stack_size = 0;
3104baa41469SJosh Poimboeuf 
3105f4f80398SJulien Thierry 			} else {
3106baa41469SJosh Poimboeuf 
3107baa41469SJosh Poimboeuf 				/* drap: push %reg */
3108e7c0219bSPeter Zijlstra 				save_reg(cfi, op->src.reg, CFI_BP, -cfi->stack_size);
3109baa41469SJosh Poimboeuf 			}
3110baa41469SJosh Poimboeuf 
3111baa41469SJosh Poimboeuf 		} else {
3112baa41469SJosh Poimboeuf 
3113baa41469SJosh Poimboeuf 			/* push %reg */
3114e7c0219bSPeter Zijlstra 			save_reg(cfi, op->src.reg, CFI_CFA, -cfi->stack_size);
3115baa41469SJosh Poimboeuf 		}
3116baa41469SJosh Poimboeuf 
3117baa41469SJosh Poimboeuf 		/* detect when asm code uses rbp as a scratch register */
3118dbcdbdfdSPeter Zijlstra 		if (opts.stackval && insn_func(insn) && op->src.reg == CFI_BP &&
3119baa41469SJosh Poimboeuf 		    cfa->base != CFI_BP)
3120e7c0219bSPeter Zijlstra 			cfi->bp_scratch = true;
3121baa41469SJosh Poimboeuf 		break;
3122baa41469SJosh Poimboeuf 
3123baa41469SJosh Poimboeuf 	case OP_DEST_REG_INDIRECT:
3124baa41469SJosh Poimboeuf 
3125e7c0219bSPeter Zijlstra 		if (cfi->drap) {
3126e7c0219bSPeter Zijlstra 			if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
3127baa41469SJosh Poimboeuf 
3128baa41469SJosh Poimboeuf 				/* drap: mov %drap, disp(%rbp) */
3129baa41469SJosh Poimboeuf 				cfa->base = CFI_BP_INDIRECT;
3130baa41469SJosh Poimboeuf 				cfa->offset = op->dest.offset;
3131baa41469SJosh Poimboeuf 
3132bf4d1a83SJosh Poimboeuf 				/* save drap offset so we know when to restore it */
3133e7c0219bSPeter Zijlstra 				cfi->drap_offset = op->dest.offset;
3134f4f80398SJulien Thierry 			} else {
3135baa41469SJosh Poimboeuf 
3136baa41469SJosh Poimboeuf 				/* drap: mov reg, disp(%rbp) */
3137e7c0219bSPeter Zijlstra 				save_reg(cfi, op->src.reg, CFI_BP, op->dest.offset);
3138baa41469SJosh Poimboeuf 			}
3139baa41469SJosh Poimboeuf 
3140baa41469SJosh Poimboeuf 		} else if (op->dest.reg == cfa->base) {
3141baa41469SJosh Poimboeuf 
3142baa41469SJosh Poimboeuf 			/* mov reg, disp(%rbp) */
3143baa41469SJosh Poimboeuf 			/* mov reg, disp(%rsp) */
3144e7c0219bSPeter Zijlstra 			save_reg(cfi, op->src.reg, CFI_CFA,
3145e7c0219bSPeter Zijlstra 				 op->dest.offset - cfi->cfa.offset);
3146201ef5a9SJulien Thierry 
3147201ef5a9SJulien Thierry 		} else if (op->dest.reg == CFI_SP) {
3148201ef5a9SJulien Thierry 
3149201ef5a9SJulien Thierry 			/* mov reg, disp(%rsp) */
3150201ef5a9SJulien Thierry 			save_reg(cfi, op->src.reg, CFI_CFA,
3151201ef5a9SJulien Thierry 				 op->dest.offset - cfi->stack_size);
3152aafeb14eSPeter Zijlstra 
3153aafeb14eSPeter Zijlstra 		} else if (op->src.reg == CFI_SP && op->dest.offset == 0) {
3154aafeb14eSPeter Zijlstra 
3155aafeb14eSPeter Zijlstra 			/* mov %rsp, (%reg); # setup a stack swizzle. */
3156aafeb14eSPeter Zijlstra 			cfi->vals[op->dest.reg].base = CFI_SP_INDIRECT;
3157aafeb14eSPeter Zijlstra 			cfi->vals[op->dest.reg].offset = cfa->offset;
3158baa41469SJosh Poimboeuf 		}
3159baa41469SJosh Poimboeuf 
3160baa41469SJosh Poimboeuf 		break;
3161baa41469SJosh Poimboeuf 
3162baa41469SJosh Poimboeuf 	case OP_DEST_MEM:
3163ea24213dSPeter Zijlstra 		if (op->src.type != OP_SRC_POP && op->src.type != OP_SRC_POPF) {
3164246b2c85SJosh Poimboeuf 			WARN_INSN(insn, "unknown stack-related memory operation");
3165baa41469SJosh Poimboeuf 			return -1;
3166baa41469SJosh Poimboeuf 		}
3167baa41469SJosh Poimboeuf 
3168baa41469SJosh Poimboeuf 		/* pop mem */
3169e7c0219bSPeter Zijlstra 		cfi->stack_size -= 8;
3170baa41469SJosh Poimboeuf 		if (cfa->base == CFI_SP)
3171baa41469SJosh Poimboeuf 			cfa->offset -= 8;
3172baa41469SJosh Poimboeuf 
3173baa41469SJosh Poimboeuf 		break;
3174baa41469SJosh Poimboeuf 
3175baa41469SJosh Poimboeuf 	default:
3176246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "unknown stack-related instruction");
3177baa41469SJosh Poimboeuf 		return -1;
3178baa41469SJosh Poimboeuf 	}
3179baa41469SJosh Poimboeuf 
3180baa41469SJosh Poimboeuf 	return 0;
3181baa41469SJosh Poimboeuf }
3182baa41469SJosh Poimboeuf 
3183c9c324dcSJosh Poimboeuf /*
3184c9c324dcSJosh Poimboeuf  * The stack layouts of alternatives instructions can sometimes diverge when
3185c9c324dcSJosh Poimboeuf  * they have stack modifications.  That's fine as long as the potential stack
3186c9c324dcSJosh Poimboeuf  * layouts don't conflict at any given potential instruction boundary.
3187c9c324dcSJosh Poimboeuf  *
3188c9c324dcSJosh Poimboeuf  * Flatten the CFIs of the different alternative code streams (both original
3189c9c324dcSJosh Poimboeuf  * and replacement) into a single shared CFI array which can be used to detect
3190c9c324dcSJosh Poimboeuf  * conflicts and nicely feed a linear array of ORC entries to the unwinder.
3191c9c324dcSJosh Poimboeuf  */
propagate_alt_cfi(struct objtool_file * file,struct instruction * insn)3192c9c324dcSJosh Poimboeuf static int propagate_alt_cfi(struct objtool_file *file, struct instruction *insn)
3193c9c324dcSJosh Poimboeuf {
3194c9c324dcSJosh Poimboeuf 	struct cfi_state **alt_cfi;
3195c9c324dcSJosh Poimboeuf 	int group_off;
3196c9c324dcSJosh Poimboeuf 
3197c9c324dcSJosh Poimboeuf 	if (!insn->alt_group)
3198c9c324dcSJosh Poimboeuf 		return 0;
3199c9c324dcSJosh Poimboeuf 
32008b946cc3SPeter Zijlstra 	if (!insn->cfi) {
32018b946cc3SPeter Zijlstra 		WARN("CFI missing");
32028b946cc3SPeter Zijlstra 		return -1;
32038b946cc3SPeter Zijlstra 	}
32048b946cc3SPeter Zijlstra 
3205c9c324dcSJosh Poimboeuf 	alt_cfi = insn->alt_group->cfi;
3206c9c324dcSJosh Poimboeuf 	group_off = insn->offset - insn->alt_group->first_insn->offset;
3207c9c324dcSJosh Poimboeuf 
3208c9c324dcSJosh Poimboeuf 	if (!alt_cfi[group_off]) {
32098b946cc3SPeter Zijlstra 		alt_cfi[group_off] = insn->cfi;
3210c9c324dcSJosh Poimboeuf 	} else {
32118b946cc3SPeter Zijlstra 		if (cficmp(alt_cfi[group_off], insn->cfi)) {
3212a706bb08SPeter Zijlstra 			struct alt_group *orig_group = insn->alt_group->orig_group ?: insn->alt_group;
3213a706bb08SPeter Zijlstra 			struct instruction *orig = orig_group->first_insn;
3214c5995abeSJosh Poimboeuf 			WARN_INSN(orig, "stack layout conflict in alternatives: %s",
3215c5995abeSJosh Poimboeuf 				  offstr(insn->sec, insn->offset));
3216c9c324dcSJosh Poimboeuf 			return -1;
3217c9c324dcSJosh Poimboeuf 		}
3218c9c324dcSJosh Poimboeuf 	}
3219c9c324dcSJosh Poimboeuf 
3220c9c324dcSJosh Poimboeuf 	return 0;
3221c9c324dcSJosh Poimboeuf }
3222c9c324dcSJosh Poimboeuf 
handle_insn_ops(struct instruction * insn,struct instruction * next_insn,struct insn_state * state)3223d54dba41SPeter Zijlstra static int handle_insn_ops(struct instruction *insn,
3224d54dba41SPeter Zijlstra 			   struct instruction *next_insn,
3225d54dba41SPeter Zijlstra 			   struct insn_state *state)
322665ea47dcSJulien Thierry {
322765ea47dcSJulien Thierry 	struct stack_op *op;
3228c5995abeSJosh Poimboeuf 	int ret;
322965ea47dcSJulien Thierry 
32303ee88df1SPeter Zijlstra 	for (op = insn->stack_ops; op; op = op->next) {
323165ea47dcSJulien Thierry 
3232c5995abeSJosh Poimboeuf 		ret = update_cfi_state(insn, next_insn, &state->cfi, op);
3233c5995abeSJosh Poimboeuf 		if (ret)
3234c5995abeSJosh Poimboeuf 			return ret;
3235ab3852abSPeter Zijlstra 
3236e1a9dda7SJosh Poimboeuf 		if (!opts.uaccess || !insn->alt_group)
3237ba08abcaSPeter Zijlstra 			continue;
3238ba08abcaSPeter Zijlstra 
323965ea47dcSJulien Thierry 		if (op->dest.type == OP_DEST_PUSHF) {
324065ea47dcSJulien Thierry 			if (!state->uaccess_stack) {
324165ea47dcSJulien Thierry 				state->uaccess_stack = 1;
324265ea47dcSJulien Thierry 			} else if (state->uaccess_stack >> 31) {
3243246b2c85SJosh Poimboeuf 				WARN_INSN(insn, "PUSHF stack exhausted");
324465ea47dcSJulien Thierry 				return 1;
324565ea47dcSJulien Thierry 			}
324665ea47dcSJulien Thierry 			state->uaccess_stack <<= 1;
324765ea47dcSJulien Thierry 			state->uaccess_stack  |= state->uaccess;
324865ea47dcSJulien Thierry 		}
324965ea47dcSJulien Thierry 
325065ea47dcSJulien Thierry 		if (op->src.type == OP_SRC_POPF) {
325165ea47dcSJulien Thierry 			if (state->uaccess_stack) {
325265ea47dcSJulien Thierry 				state->uaccess = state->uaccess_stack & 1;
325365ea47dcSJulien Thierry 				state->uaccess_stack >>= 1;
325465ea47dcSJulien Thierry 				if (state->uaccess_stack == 1)
325565ea47dcSJulien Thierry 					state->uaccess_stack = 0;
325665ea47dcSJulien Thierry 			}
325765ea47dcSJulien Thierry 		}
325865ea47dcSJulien Thierry 	}
325965ea47dcSJulien Thierry 
326065ea47dcSJulien Thierry 	return 0;
326165ea47dcSJulien Thierry }
326265ea47dcSJulien Thierry 
insn_cfi_match(struct instruction * insn,struct cfi_state * cfi2)3263e7c0219bSPeter Zijlstra static bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2)
3264baa41469SJosh Poimboeuf {
32658b946cc3SPeter Zijlstra 	struct cfi_state *cfi1 = insn->cfi;
3266baa41469SJosh Poimboeuf 	int i;
3267baa41469SJosh Poimboeuf 
32688b946cc3SPeter Zijlstra 	if (!cfi1) {
32698b946cc3SPeter Zijlstra 		WARN("CFI missing");
32708b946cc3SPeter Zijlstra 		return false;
32718b946cc3SPeter Zijlstra 	}
32728b946cc3SPeter Zijlstra 
3273e7c0219bSPeter Zijlstra 	if (memcmp(&cfi1->cfa, &cfi2->cfa, sizeof(cfi1->cfa))) {
3274e7c0219bSPeter Zijlstra 
3275246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "stack state mismatch: cfa1=%d%+d cfa2=%d%+d",
3276e7c0219bSPeter Zijlstra 			  cfi1->cfa.base, cfi1->cfa.offset,
3277e7c0219bSPeter Zijlstra 			  cfi2->cfa.base, cfi2->cfa.offset);
3278c5995abeSJosh Poimboeuf 		return false;
3279baa41469SJosh Poimboeuf 
3280c5995abeSJosh Poimboeuf 	}
3281c5995abeSJosh Poimboeuf 
3282c5995abeSJosh Poimboeuf 	if (memcmp(&cfi1->regs, &cfi2->regs, sizeof(cfi1->regs))) {
3283baa41469SJosh Poimboeuf 		for (i = 0; i < CFI_NUM_REGS; i++) {
3284c5995abeSJosh Poimboeuf 
3285c5995abeSJosh Poimboeuf 			if (!memcmp(&cfi1->regs[i], &cfi2->regs[i], sizeof(struct cfi_reg)))
3286baa41469SJosh Poimboeuf 				continue;
3287baa41469SJosh Poimboeuf 
3288246b2c85SJosh Poimboeuf 			WARN_INSN(insn, "stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d",
3289e7c0219bSPeter Zijlstra 				  i, cfi1->regs[i].base, cfi1->regs[i].offset,
3290e7c0219bSPeter Zijlstra 				  i, cfi2->regs[i].base, cfi2->regs[i].offset);
3291c5995abeSJosh Poimboeuf 		}
3292c5995abeSJosh Poimboeuf 		return false;
3293baa41469SJosh Poimboeuf 	}
3294baa41469SJosh Poimboeuf 
3295c5995abeSJosh Poimboeuf 	if (cfi1->type != cfi2->type) {
3296627fce14SJosh Poimboeuf 
3297246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "stack state mismatch: type1=%d type2=%d",
3298246b2c85SJosh Poimboeuf 			  cfi1->type, cfi2->type);
3299c5995abeSJosh Poimboeuf 		return false;
3300c5995abeSJosh Poimboeuf 	}
3301e7c0219bSPeter Zijlstra 
3302c5995abeSJosh Poimboeuf 	if (cfi1->drap != cfi2->drap ||
3303e7c0219bSPeter Zijlstra 		   (cfi1->drap && cfi1->drap_reg != cfi2->drap_reg) ||
3304e7c0219bSPeter Zijlstra 		   (cfi1->drap && cfi1->drap_offset != cfi2->drap_offset)) {
3305e7c0219bSPeter Zijlstra 
3306246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "stack state mismatch: drap1=%d(%d,%d) drap2=%d(%d,%d)",
3307e7c0219bSPeter Zijlstra 			  cfi1->drap, cfi1->drap_reg, cfi1->drap_offset,
3308e7c0219bSPeter Zijlstra 			  cfi2->drap, cfi2->drap_reg, cfi2->drap_offset);
3309baa41469SJosh Poimboeuf 		return false;
3310dcc914f4SJosh Poimboeuf 	}
3311dcc914f4SJosh Poimboeuf 
3312c5995abeSJosh Poimboeuf 	return true;
3313c5995abeSJosh Poimboeuf }
3314c5995abeSJosh Poimboeuf 
func_uaccess_safe(struct symbol * func)3315ea24213dSPeter Zijlstra static inline bool func_uaccess_safe(struct symbol *func)
3316ea24213dSPeter Zijlstra {
3317ea24213dSPeter Zijlstra 	if (func)
3318e10cd8feSJosh Poimboeuf 		return func->uaccess_safe;
3319ea24213dSPeter Zijlstra 
3320ea24213dSPeter Zijlstra 	return false;
3321ea24213dSPeter Zijlstra }
3322ea24213dSPeter Zijlstra 
call_dest_name(struct instruction * insn)33230c1ddd33SJosh Poimboeuf static inline const char *call_dest_name(struct instruction *insn)
3324ea24213dSPeter Zijlstra {
332582880283SSergei Trofimovich 	static char pvname[19];
3326a5bd6236SJosh Poimboeuf 	struct reloc *reloc;
3327db2b0c5dSPeter Zijlstra 	int idx;
3328db2b0c5dSPeter Zijlstra 
3329c6f5dc28SPeter Zijlstra 	if (insn_call_dest(insn))
3330c6f5dc28SPeter Zijlstra 		return insn_call_dest(insn)->name;
3331ea24213dSPeter Zijlstra 
3332a5bd6236SJosh Poimboeuf 	reloc = insn_reloc(NULL, insn);
3333a5bd6236SJosh Poimboeuf 	if (reloc && !strcmp(reloc->sym->name, "pv_ops")) {
33340696b6e3SJosh Poimboeuf 		idx = (reloc_addend(reloc) / sizeof(void *));
3335db2b0c5dSPeter Zijlstra 		snprintf(pvname, sizeof(pvname), "pv_ops[%d]", idx);
3336db2b0c5dSPeter Zijlstra 		return pvname;
3337db2b0c5dSPeter Zijlstra 	}
3338db2b0c5dSPeter Zijlstra 
3339ea24213dSPeter Zijlstra 	return "{dynamic}";
3340ea24213dSPeter Zijlstra }
3341ea24213dSPeter Zijlstra 
pv_call_dest(struct objtool_file * file,struct instruction * insn)3342db2b0c5dSPeter Zijlstra static bool pv_call_dest(struct objtool_file *file, struct instruction *insn)
3343db2b0c5dSPeter Zijlstra {
3344db2b0c5dSPeter Zijlstra 	struct symbol *target;
3345a5bd6236SJosh Poimboeuf 	struct reloc *reloc;
3346db2b0c5dSPeter Zijlstra 	int idx;
3347db2b0c5dSPeter Zijlstra 
3348a5bd6236SJosh Poimboeuf 	reloc = insn_reloc(file, insn);
3349a5bd6236SJosh Poimboeuf 	if (!reloc || strcmp(reloc->sym->name, "pv_ops"))
3350db2b0c5dSPeter Zijlstra 		return false;
3351db2b0c5dSPeter Zijlstra 
33520696b6e3SJosh Poimboeuf 	idx = (arch_dest_reloc_offset(reloc_addend(reloc)) / sizeof(void *));
3353db2b0c5dSPeter Zijlstra 
3354db2b0c5dSPeter Zijlstra 	if (file->pv_ops[idx].clean)
3355db2b0c5dSPeter Zijlstra 		return true;
3356db2b0c5dSPeter Zijlstra 
3357db2b0c5dSPeter Zijlstra 	file->pv_ops[idx].clean = true;
3358db2b0c5dSPeter Zijlstra 
3359db2b0c5dSPeter Zijlstra 	list_for_each_entry(target, &file->pv_ops[idx].targets, pv_target) {
3360db2b0c5dSPeter Zijlstra 		if (!target->sec->noinstr) {
3361db2b0c5dSPeter Zijlstra 			WARN("pv_ops[%d]: %s", idx, target->name);
3362db2b0c5dSPeter Zijlstra 			file->pv_ops[idx].clean = false;
3363db2b0c5dSPeter Zijlstra 		}
3364db2b0c5dSPeter Zijlstra 	}
3365db2b0c5dSPeter Zijlstra 
3366db2b0c5dSPeter Zijlstra 	return file->pv_ops[idx].clean;
3367db2b0c5dSPeter Zijlstra }
3368db2b0c5dSPeter Zijlstra 
noinstr_call_dest(struct objtool_file * file,struct instruction * insn,struct symbol * func)3369db2b0c5dSPeter Zijlstra static inline bool noinstr_call_dest(struct objtool_file *file,
3370db2b0c5dSPeter Zijlstra 				     struct instruction *insn,
3371db2b0c5dSPeter Zijlstra 				     struct symbol *func)
33726b643a07SPeter Zijlstra {
33736b643a07SPeter Zijlstra 	/*
33746b643a07SPeter Zijlstra 	 * We can't deal with indirect function calls at present;
33756b643a07SPeter Zijlstra 	 * assume they're instrumented.
33766b643a07SPeter Zijlstra 	 */
3377db2b0c5dSPeter Zijlstra 	if (!func) {
3378db2b0c5dSPeter Zijlstra 		if (file->pv_ops)
3379db2b0c5dSPeter Zijlstra 			return pv_call_dest(file, insn);
3380db2b0c5dSPeter Zijlstra 
33816b643a07SPeter Zijlstra 		return false;
3382db2b0c5dSPeter Zijlstra 	}
33836b643a07SPeter Zijlstra 
33846b643a07SPeter Zijlstra 	/*
33856b643a07SPeter Zijlstra 	 * If the symbol is from a noinstr section; we good.
33866b643a07SPeter Zijlstra 	 */
33876b643a07SPeter Zijlstra 	if (func->sec->noinstr)
33886b643a07SPeter Zijlstra 		return true;
33896b643a07SPeter Zijlstra 
33906b643a07SPeter Zijlstra 	/*
33912b5a0e42SPeter Zijlstra 	 * If the symbol is a static_call trampoline, we can't tell.
33922b5a0e42SPeter Zijlstra 	 */
33932b5a0e42SPeter Zijlstra 	if (func->static_call_tramp)
33942b5a0e42SPeter Zijlstra 		return true;
33952b5a0e42SPeter Zijlstra 
33962b5a0e42SPeter Zijlstra 	/*
33976b643a07SPeter Zijlstra 	 * The __ubsan_handle_*() calls are like WARN(), they only happen when
33986b643a07SPeter Zijlstra 	 * something 'BAD' happened. At the risk of taking the machine down,
33996b643a07SPeter Zijlstra 	 * let them proceed to get the message out.
34006b643a07SPeter Zijlstra 	 */
34016b643a07SPeter Zijlstra 	if (!strncmp(func->name, "__ubsan_handle_", 15))
34026b643a07SPeter Zijlstra 		return true;
34036b643a07SPeter Zijlstra 
34046b643a07SPeter Zijlstra 	return false;
34056b643a07SPeter Zijlstra }
34066b643a07SPeter Zijlstra 
validate_call(struct objtool_file * file,struct instruction * insn,struct insn_state * state)3407db2b0c5dSPeter Zijlstra static int validate_call(struct objtool_file *file,
3408db2b0c5dSPeter Zijlstra 			 struct instruction *insn,
3409db2b0c5dSPeter Zijlstra 			 struct insn_state *state)
3410ea24213dSPeter Zijlstra {
3411c4a33939SPeter Zijlstra 	if (state->noinstr && state->instr <= 0 &&
3412c6f5dc28SPeter Zijlstra 	    !noinstr_call_dest(file, insn, insn_call_dest(insn))) {
3413246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "call to %s() leaves .noinstr.text section", call_dest_name(insn));
3414c4a33939SPeter Zijlstra 		return 1;
3415c4a33939SPeter Zijlstra 	}
3416c4a33939SPeter Zijlstra 
3417c6f5dc28SPeter Zijlstra 	if (state->uaccess && !func_uaccess_safe(insn_call_dest(insn))) {
3418246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "call to %s() with UACCESS enabled", call_dest_name(insn));
3419ea24213dSPeter Zijlstra 		return 1;
3420ea24213dSPeter Zijlstra 	}
3421ea24213dSPeter Zijlstra 
34222f0f9e9aSPeter Zijlstra 	if (state->df) {
3423246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "call to %s() with DF set", call_dest_name(insn));
34242f0f9e9aSPeter Zijlstra 		return 1;
34252f0f9e9aSPeter Zijlstra 	}
34262f0f9e9aSPeter Zijlstra 
3427ea24213dSPeter Zijlstra 	return 0;
3428ea24213dSPeter Zijlstra }
3429ea24213dSPeter Zijlstra 
validate_sibling_call(struct objtool_file * file,struct instruction * insn,struct insn_state * state)3430db2b0c5dSPeter Zijlstra static int validate_sibling_call(struct objtool_file *file,
3431db2b0c5dSPeter Zijlstra 				 struct instruction *insn,
3432db2b0c5dSPeter Zijlstra 				 struct insn_state *state)
343354262aa2SPeter Zijlstra {
34345a9c361aSPeter Zijlstra 	if (insn_func(insn) && has_modified_stack_frame(insn, state)) {
3435246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "sibling call from callable instruction with modified stack frame");
343654262aa2SPeter Zijlstra 		return 1;
343754262aa2SPeter Zijlstra 	}
343854262aa2SPeter Zijlstra 
3439db2b0c5dSPeter Zijlstra 	return validate_call(file, insn, state);
344054262aa2SPeter Zijlstra }
344154262aa2SPeter Zijlstra 
validate_return(struct symbol * func,struct instruction * insn,struct insn_state * state)3442a92e92d1SPeter Zijlstra static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
3443a92e92d1SPeter Zijlstra {
3444c4a33939SPeter Zijlstra 	if (state->noinstr && state->instr > 0) {
3445246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "return with instrumentation enabled");
3446c4a33939SPeter Zijlstra 		return 1;
3447c4a33939SPeter Zijlstra 	}
3448c4a33939SPeter Zijlstra 
3449a92e92d1SPeter Zijlstra 	if (state->uaccess && !func_uaccess_safe(func)) {
3450246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "return with UACCESS enabled");
3451a92e92d1SPeter Zijlstra 		return 1;
3452a92e92d1SPeter Zijlstra 	}
3453a92e92d1SPeter Zijlstra 
3454a92e92d1SPeter Zijlstra 	if (!state->uaccess && func_uaccess_safe(func)) {
3455246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "return with UACCESS disabled from a UACCESS-safe function");
3456a92e92d1SPeter Zijlstra 		return 1;
3457a92e92d1SPeter Zijlstra 	}
3458a92e92d1SPeter Zijlstra 
3459a92e92d1SPeter Zijlstra 	if (state->df) {
3460246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "return with DF set");
3461a92e92d1SPeter Zijlstra 		return 1;
3462a92e92d1SPeter Zijlstra 	}
3463a92e92d1SPeter Zijlstra 
3464e25eea89SPeter Zijlstra 	if (func && has_modified_stack_frame(insn, state)) {
3465246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "return with modified stack frame");
3466a92e92d1SPeter Zijlstra 		return 1;
3467a92e92d1SPeter Zijlstra 	}
3468a92e92d1SPeter Zijlstra 
3469e7c0219bSPeter Zijlstra 	if (state->cfi.bp_scratch) {
3470246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "BP used as a scratch register");
3471a92e92d1SPeter Zijlstra 		return 1;
3472a92e92d1SPeter Zijlstra 	}
3473a92e92d1SPeter Zijlstra 
3474a92e92d1SPeter Zijlstra 	return 0;
3475a92e92d1SPeter Zijlstra }
3476a92e92d1SPeter Zijlstra 
next_insn_to_validate(struct objtool_file * file,struct instruction * insn)3477c9c324dcSJosh Poimboeuf static struct instruction *next_insn_to_validate(struct objtool_file *file,
3478c9c324dcSJosh Poimboeuf 						 struct instruction *insn)
34797117f16bSPeter Zijlstra {
3480b23cc71cSJosh Poimboeuf 	struct alt_group *alt_group = insn->alt_group;
34817117f16bSPeter Zijlstra 
3482c9c324dcSJosh Poimboeuf 	/*
3483c9c324dcSJosh Poimboeuf 	 * Simulate the fact that alternatives are patched in-place.  When the
3484c9c324dcSJosh Poimboeuf 	 * end of a replacement alt_group is reached, redirect objtool flow to
3485c9c324dcSJosh Poimboeuf 	 * the end of the original alt_group.
34861c34496eSPeter Zijlstra 	 *
34871c34496eSPeter Zijlstra 	 * insn->alts->insn -> alt_group->first_insn
34881c34496eSPeter Zijlstra 	 *		       ...
34891c34496eSPeter Zijlstra 	 *		       alt_group->last_insn
34901c34496eSPeter Zijlstra 	 *		       [alt_group->nop]      -> next(orig_group->last_insn)
3491c9c324dcSJosh Poimboeuf 	 */
34921c34496eSPeter Zijlstra 	if (alt_group) {
34931c34496eSPeter Zijlstra 		if (alt_group->nop) {
34941c34496eSPeter Zijlstra 			/* ->nop implies ->orig_group */
34951c34496eSPeter Zijlstra 			if (insn == alt_group->last_insn)
34961c34496eSPeter Zijlstra 				return alt_group->nop;
34971c34496eSPeter Zijlstra 			if (insn == alt_group->nop)
34981c34496eSPeter Zijlstra 				goto next_orig;
34991c34496eSPeter Zijlstra 		}
35001c34496eSPeter Zijlstra 		if (insn == alt_group->last_insn && alt_group->orig_group)
35011c34496eSPeter Zijlstra 			goto next_orig;
35021c34496eSPeter Zijlstra 	}
3503c9c324dcSJosh Poimboeuf 
3504c9c324dcSJosh Poimboeuf 	return next_insn_same_sec(file, insn);
35051c34496eSPeter Zijlstra 
35061c34496eSPeter Zijlstra next_orig:
35071c34496eSPeter Zijlstra 	return next_insn_same_sec(file, alt_group->orig_group->last_insn);
35087117f16bSPeter Zijlstra }
35097117f16bSPeter Zijlstra 
skip_alt_group(struct instruction * insn)35102d12c6fbSJosh Poimboeuf static bool skip_alt_group(struct instruction *insn)
35112d12c6fbSJosh Poimboeuf {
35122d12c6fbSJosh Poimboeuf 	struct instruction *alt_insn = insn->alts ? insn->alts->insn : NULL;
35132d12c6fbSJosh Poimboeuf 
35142d12c6fbSJosh Poimboeuf 	/* ANNOTATE_IGNORE_ALTERNATIVE */
35152d12c6fbSJosh Poimboeuf 	if (insn->alt_group && insn->alt_group->ignore)
35162d12c6fbSJosh Poimboeuf 		return true;
35172d12c6fbSJosh Poimboeuf 
35182d12c6fbSJosh Poimboeuf 	/*
35192d12c6fbSJosh Poimboeuf 	 * For NOP patched with CLAC/STAC, only follow the latter to avoid
35202d12c6fbSJosh Poimboeuf 	 * impossible code paths combining patched CLAC with unpatched STAC
35212d12c6fbSJosh Poimboeuf 	 * or vice versa.
35222d12c6fbSJosh Poimboeuf 	 *
35232d12c6fbSJosh Poimboeuf 	 * ANNOTATE_IGNORE_ALTERNATIVE could have been used here, but Linus
35242d12c6fbSJosh Poimboeuf 	 * requested not to do that to avoid hurting .s file readability
35252d12c6fbSJosh Poimboeuf 	 * around CLAC/STAC alternative sites.
35262d12c6fbSJosh Poimboeuf 	 */
35272d12c6fbSJosh Poimboeuf 
35282d12c6fbSJosh Poimboeuf 	if (!alt_insn)
35292d12c6fbSJosh Poimboeuf 		return false;
35302d12c6fbSJosh Poimboeuf 
35312d12c6fbSJosh Poimboeuf 	/* Don't override ASM_{CLAC,STAC}_UNSAFE */
35322d12c6fbSJosh Poimboeuf 	if (alt_insn->alt_group && alt_insn->alt_group->ignore)
35332d12c6fbSJosh Poimboeuf 		return false;
35342d12c6fbSJosh Poimboeuf 
35352d12c6fbSJosh Poimboeuf 	return alt_insn->type == INSN_CLAC || alt_insn->type == INSN_STAC;
35362d12c6fbSJosh Poimboeuf }
35372d12c6fbSJosh Poimboeuf 
35387117f16bSPeter Zijlstra /*
3539dcc914f4SJosh Poimboeuf  * Follow the branch starting at the given instruction, and recursively follow
3540dcc914f4SJosh Poimboeuf  * any other branches (jumps).  Meanwhile, track the frame pointer state at
3541dcc914f4SJosh Poimboeuf  * each instruction and validate all the rules described in
3542d6a21f2dSMauro Carvalho Chehab  * tools/objtool/Documentation/objtool.txt.
3543dcc914f4SJosh Poimboeuf  */
validate_branch(struct objtool_file * file,struct symbol * func,struct instruction * insn,struct insn_state state)3544c705ceccSJosh Poimboeuf static int validate_branch(struct objtool_file *file, struct symbol *func,
3545b7460462SPeter Zijlstra 			   struct instruction *insn, struct insn_state state)
3546dcc914f4SJosh Poimboeuf {
3547dcc914f4SJosh Poimboeuf 	struct alternative *alt;
35488b946cc3SPeter Zijlstra 	struct instruction *next_insn, *prev_insn = NULL;
3549dcc914f4SJosh Poimboeuf 	struct section *sec;
3550882a0db9SPeter Zijlstra 	u8 visited;
3551dcc914f4SJosh Poimboeuf 	int ret;
3552dcc914f4SJosh Poimboeuf 
3553c84301d7SJosh Poimboeuf 	if (func && func->ignore)
3554c84301d7SJosh Poimboeuf 		return 0;
3555c84301d7SJosh Poimboeuf 
3556dcc914f4SJosh Poimboeuf 	sec = insn->sec;
3557dcc914f4SJosh Poimboeuf 
3558dcc914f4SJosh Poimboeuf 	while (1) {
3559c9c324dcSJosh Poimboeuf 		next_insn = next_insn_to_validate(file, insn);
356039358a03SJosh Poimboeuf 
3561dbcdbdfdSPeter Zijlstra 		if (func && insn_func(insn) && func != insn_func(insn)->pfunc) {
35623c68a92dSSami Tolvanen 			/* Ignore KCFI type preambles, which always fall through */
35639f2899feSPeter Zijlstra 			if (!strncmp(func->name, "__cfi_", 6) ||
35649f2899feSPeter Zijlstra 			    !strncmp(func->name, "__pfx_", 6))
35653c68a92dSSami Tolvanen 				return 0;
35663c68a92dSSami Tolvanen 
35676b023c78SJosh Poimboeuf 			if (file->ignore_unreachables)
35686b023c78SJosh Poimboeuf 				return 0;
35696b023c78SJosh Poimboeuf 
3570dcc914f4SJosh Poimboeuf 			WARN("%s() falls through to next function %s()",
3571dbcdbdfdSPeter Zijlstra 			     func->name, insn_func(insn)->name);
3572c5610071SJosh Poimboeuf 			func->warned = 1;
3573c5995abeSJosh Poimboeuf 
3574dcc914f4SJosh Poimboeuf 			return 1;
3575dcc914f4SJosh Poimboeuf 		}
3576dcc914f4SJosh Poimboeuf 
3577a09a6e23SPeter Zijlstra 		visited = VISITED_BRANCH << state.uaccess;
3578a09a6e23SPeter Zijlstra 		if (insn->visited & VISITED_BRANCH_MASK) {
3579e7c0219bSPeter Zijlstra 			if (!insn->hint && !insn_cfi_match(insn, &state.cfi))
3580dcc914f4SJosh Poimboeuf 				return 1;
3581dcc914f4SJosh Poimboeuf 
3582882a0db9SPeter Zijlstra 			if (insn->visited & visited)
3583dcc914f4SJosh Poimboeuf 				return 0;
35848b946cc3SPeter Zijlstra 		} else {
35858b946cc3SPeter Zijlstra 			nr_insns_visited++;
3586dcc914f4SJosh Poimboeuf 		}
3587dcc914f4SJosh Poimboeuf 
3588c4a33939SPeter Zijlstra 		if (state.noinstr)
3589c4a33939SPeter Zijlstra 			state.instr += insn->instr;
3590c4a33939SPeter Zijlstra 
35918b946cc3SPeter Zijlstra 		if (insn->hint) {
35928faea26eSJosh Poimboeuf 			if (insn->restore) {
35938faea26eSJosh Poimboeuf 				struct instruction *save_insn, *i;
35948faea26eSJosh Poimboeuf 
35958faea26eSJosh Poimboeuf 				i = insn;
35968faea26eSJosh Poimboeuf 				save_insn = NULL;
35978faea26eSJosh Poimboeuf 
35988faea26eSJosh Poimboeuf 				sym_for_each_insn_continue_reverse(file, func, i) {
35998faea26eSJosh Poimboeuf 					if (i->save) {
36008faea26eSJosh Poimboeuf 						save_insn = i;
36018faea26eSJosh Poimboeuf 						break;
36028faea26eSJosh Poimboeuf 					}
36038faea26eSJosh Poimboeuf 				}
36048faea26eSJosh Poimboeuf 
36058faea26eSJosh Poimboeuf 				if (!save_insn) {
3606246b2c85SJosh Poimboeuf 					WARN_INSN(insn, "no corresponding CFI save for CFI restore");
36078faea26eSJosh Poimboeuf 					return 1;
36088faea26eSJosh Poimboeuf 				}
36098faea26eSJosh Poimboeuf 
36108faea26eSJosh Poimboeuf 				if (!save_insn->visited) {
361110b4c4bcSJosh Poimboeuf 					/*
361210b4c4bcSJosh Poimboeuf 					 * If the restore hint insn is at the
361310b4c4bcSJosh Poimboeuf 					 * beginning of a basic block and was
361410b4c4bcSJosh Poimboeuf 					 * branched to from elsewhere, and the
361510b4c4bcSJosh Poimboeuf 					 * save insn hasn't been visited yet,
361610b4c4bcSJosh Poimboeuf 					 * defer following this branch for now.
361710b4c4bcSJosh Poimboeuf 					 * It will be seen later via the
361810b4c4bcSJosh Poimboeuf 					 * straight-line path.
361910b4c4bcSJosh Poimboeuf 					 */
362010b4c4bcSJosh Poimboeuf 					if (!prev_insn)
362110b4c4bcSJosh Poimboeuf 						return 0;
362210b4c4bcSJosh Poimboeuf 
3623246b2c85SJosh Poimboeuf 					WARN_INSN(insn, "objtool isn't smart enough to handle this CFI save/restore combo");
36248faea26eSJosh Poimboeuf 					return 1;
36258faea26eSJosh Poimboeuf 				}
36268faea26eSJosh Poimboeuf 
36278faea26eSJosh Poimboeuf 				insn->cfi = save_insn->cfi;
36288faea26eSJosh Poimboeuf 				nr_cfi_reused++;
36298faea26eSJosh Poimboeuf 			}
36308faea26eSJosh Poimboeuf 
36318b946cc3SPeter Zijlstra 			state.cfi = *insn->cfi;
36328b946cc3SPeter Zijlstra 		} else {
36338b946cc3SPeter Zijlstra 			/* XXX track if we actually changed state.cfi */
36348b946cc3SPeter Zijlstra 
36358b946cc3SPeter Zijlstra 			if (prev_insn && !cficmp(prev_insn->cfi, &state.cfi)) {
36368b946cc3SPeter Zijlstra 				insn->cfi = prev_insn->cfi;
36378b946cc3SPeter Zijlstra 				nr_cfi_reused++;
36388b946cc3SPeter Zijlstra 			} else {
36398b946cc3SPeter Zijlstra 				insn->cfi = cfi_hash_find_or_add(&state.cfi);
36408b946cc3SPeter Zijlstra 			}
36418b946cc3SPeter Zijlstra 		}
3642dcc914f4SJosh Poimboeuf 
3643882a0db9SPeter Zijlstra 		insn->visited |= visited;
3644baa41469SJosh Poimboeuf 
3645c9c324dcSJosh Poimboeuf 		if (propagate_alt_cfi(file, insn))
3646c9c324dcSJosh Poimboeuf 			return 1;
3647c9c324dcSJosh Poimboeuf 
36481154bbd3SJosh Poimboeuf 		if (insn->alts) {
3649d5406654SPeter Zijlstra 			for (alt = insn->alts; alt; alt = alt->next) {
3650c705ceccSJosh Poimboeuf 				ret = validate_branch(file, func, alt->insn, state);
36517697eee3SPeter Zijlstra 				if (ret) {
3652ced23d2eSJosh Poimboeuf 					BT_INSN(insn, "(alt)");
36537697eee3SPeter Zijlstra 					return ret;
3654dcc914f4SJosh Poimboeuf 				}
3655a845c7cfSJosh Poimboeuf 			}
3656baa41469SJosh Poimboeuf 		}
3657baa41469SJosh Poimboeuf 
36582d12c6fbSJosh Poimboeuf 		if (skip_alt_group(insn))
36591154bbd3SJosh Poimboeuf 			return 0;
36601154bbd3SJosh Poimboeuf 
3661d54dba41SPeter Zijlstra 		if (handle_insn_ops(insn, next_insn, &state))
366260041bcdSPeter Zijlstra 			return 1;
366360041bcdSPeter Zijlstra 
3664dcc914f4SJosh Poimboeuf 		switch (insn->type) {
3665dcc914f4SJosh Poimboeuf 
3666dcc914f4SJosh Poimboeuf 		case INSN_RETURN:
3667a92e92d1SPeter Zijlstra 			return validate_return(func, insn, &state);
3668dcc914f4SJosh Poimboeuf 
3669dcc914f4SJosh Poimboeuf 		case INSN_CALL:
3670ea24213dSPeter Zijlstra 		case INSN_CALL_DYNAMIC:
3671db2b0c5dSPeter Zijlstra 			ret = validate_call(file, insn, &state);
3672ea24213dSPeter Zijlstra 			if (ret)
3673ea24213dSPeter Zijlstra 				return ret;
3674ea24213dSPeter Zijlstra 
3675dbf46008SPeter Zijlstra 			if (opts.stackval && func && !is_special_call(insn) &&
3676c9bab22bSJosh Poimboeuf 			    !has_valid_stack_frame(&state)) {
3677246b2c85SJosh Poimboeuf 				WARN_INSN(insn, "call without frame pointer save/setup");
3678dcc914f4SJosh Poimboeuf 				return 1;
3679dcc914f4SJosh Poimboeuf 			}
3680c9bab22bSJosh Poimboeuf 
3681dcc914f4SJosh Poimboeuf 			break;
3682dcc914f4SJosh Poimboeuf 
3683dcc914f4SJosh Poimboeuf 		case INSN_JUMP_CONDITIONAL:
3684dcc914f4SJosh Poimboeuf 		case INSN_JUMP_UNCONDITIONAL:
3685ecf11ba4SJosh Poimboeuf 			if (is_sibling_call(insn)) {
3686db2b0c5dSPeter Zijlstra 				ret = validate_sibling_call(file, insn, &state);
368754262aa2SPeter Zijlstra 				if (ret)
368854262aa2SPeter Zijlstra 					return ret;
368954262aa2SPeter Zijlstra 
36900c1ddd33SJosh Poimboeuf 			} else if (insn->jump_dest) {
3691c705ceccSJosh Poimboeuf 				ret = validate_branch(file, func,
3692c705ceccSJosh Poimboeuf 						      insn->jump_dest, state);
36937697eee3SPeter Zijlstra 				if (ret) {
3694ced23d2eSJosh Poimboeuf 					BT_INSN(insn, "(branch)");
36957697eee3SPeter Zijlstra 					return ret;
36967697eee3SPeter Zijlstra 				}
36974855022aSJosh Poimboeuf 			}
3698dcc914f4SJosh Poimboeuf 
3699dcc914f4SJosh Poimboeuf 			if (insn->type == INSN_JUMP_UNCONDITIONAL)
3700dcc914f4SJosh Poimboeuf 				return 0;
3701dcc914f4SJosh Poimboeuf 
3702dcc914f4SJosh Poimboeuf 			break;
3703dcc914f4SJosh Poimboeuf 
3704dcc914f4SJosh Poimboeuf 		case INSN_JUMP_DYNAMIC:
3705b68b9907SJosh Poimboeuf 		case INSN_JUMP_DYNAMIC_CONDITIONAL:
3706ecf11ba4SJosh Poimboeuf 			if (is_sibling_call(insn)) {
3707db2b0c5dSPeter Zijlstra 				ret = validate_sibling_call(file, insn, &state);
370854262aa2SPeter Zijlstra 				if (ret)
370954262aa2SPeter Zijlstra 					return ret;
3710dcc914f4SJosh Poimboeuf 			}
3711dcc914f4SJosh Poimboeuf 
3712b68b9907SJosh Poimboeuf 			if (insn->type == INSN_JUMP_DYNAMIC)
3713dcc914f4SJosh Poimboeuf 				return 0;
3714dcc914f4SJosh Poimboeuf 
3715b68b9907SJosh Poimboeuf 			break;
3716b68b9907SJosh Poimboeuf 
3717fe1042b1SJosh Poimboeuf 		case INSN_SYSCALL:
37182dbbca9bSJosh Poimboeuf 			if (func && (!next_insn || !next_insn->hint)) {
3719246b2c85SJosh Poimboeuf 				WARN_INSN(insn, "unsupported instruction in callable function");
372039358a03SJosh Poimboeuf 				return 1;
372139358a03SJosh Poimboeuf 			}
37222dbbca9bSJosh Poimboeuf 
3723dda014baSJuergen Gross 			break;
37242dbbca9bSJosh Poimboeuf 
37252dbbca9bSJosh Poimboeuf 		case INSN_SYSRET:
37262dbbca9bSJosh Poimboeuf 			if (func && (!next_insn || !next_insn->hint)) {
37272dbbca9bSJosh Poimboeuf 				WARN_INSN(insn, "unsupported instruction in callable function");
37282dbbca9bSJosh Poimboeuf 				return 1;
3729dda014baSJuergen Gross 			}
37302dbbca9bSJosh Poimboeuf 
373139358a03SJosh Poimboeuf 			return 0;
373239358a03SJosh Poimboeuf 
3733ea24213dSPeter Zijlstra 		case INSN_STAC:
3734e1a9dda7SJosh Poimboeuf 			if (!opts.uaccess)
3735e1a9dda7SJosh Poimboeuf 				break;
3736e1a9dda7SJosh Poimboeuf 
3737ea24213dSPeter Zijlstra 			if (state.uaccess) {
3738246b2c85SJosh Poimboeuf 				WARN_INSN(insn, "recursive UACCESS enable");
3739ea24213dSPeter Zijlstra 				return 1;
3740ea24213dSPeter Zijlstra 			}
3741ea24213dSPeter Zijlstra 
3742ea24213dSPeter Zijlstra 			state.uaccess = true;
3743ea24213dSPeter Zijlstra 			break;
3744ea24213dSPeter Zijlstra 
3745ea24213dSPeter Zijlstra 		case INSN_CLAC:
3746e1a9dda7SJosh Poimboeuf 			if (!opts.uaccess)
3747e1a9dda7SJosh Poimboeuf 				break;
3748e1a9dda7SJosh Poimboeuf 
3749c705ceccSJosh Poimboeuf 			if (!state.uaccess && func) {
3750246b2c85SJosh Poimboeuf 				WARN_INSN(insn, "redundant UACCESS disable");
3751ea24213dSPeter Zijlstra 				return 1;
3752ea24213dSPeter Zijlstra 			}
3753ea24213dSPeter Zijlstra 
3754ea24213dSPeter Zijlstra 			if (func_uaccess_safe(func) && !state.uaccess_stack) {
3755246b2c85SJosh Poimboeuf 				WARN_INSN(insn, "UACCESS-safe disables UACCESS");
3756ea24213dSPeter Zijlstra 				return 1;
3757ea24213dSPeter Zijlstra 			}
3758ea24213dSPeter Zijlstra 
3759ea24213dSPeter Zijlstra 			state.uaccess = false;
3760baa41469SJosh Poimboeuf 			break;
3761baa41469SJosh Poimboeuf 
37622f0f9e9aSPeter Zijlstra 		case INSN_STD:
37636f567c93SJosh Poimboeuf 			if (state.df) {
3764246b2c85SJosh Poimboeuf 				WARN_INSN(insn, "recursive STD");
37656f567c93SJosh Poimboeuf 				return 1;
37666f567c93SJosh Poimboeuf 			}
37672f0f9e9aSPeter Zijlstra 
37682f0f9e9aSPeter Zijlstra 			state.df = true;
37692f0f9e9aSPeter Zijlstra 			break;
37702f0f9e9aSPeter Zijlstra 
37712f0f9e9aSPeter Zijlstra 		case INSN_CLD:
37726f567c93SJosh Poimboeuf 			if (!state.df && func) {
3773246b2c85SJosh Poimboeuf 				WARN_INSN(insn, "redundant CLD");
37746f567c93SJosh Poimboeuf 				return 1;
37756f567c93SJosh Poimboeuf 			}
37762f0f9e9aSPeter Zijlstra 
37772f0f9e9aSPeter Zijlstra 			state.df = false;
3778dcc914f4SJosh Poimboeuf 			break;
3779dcc914f4SJosh Poimboeuf 
3780dcc914f4SJosh Poimboeuf 		default:
3781dcc914f4SJosh Poimboeuf 			break;
3782dcc914f4SJosh Poimboeuf 		}
3783dcc914f4SJosh Poimboeuf 
3784dcc914f4SJosh Poimboeuf 		if (insn->dead_end)
3785dcc914f4SJosh Poimboeuf 			return 0;
3786dcc914f4SJosh Poimboeuf 
378700d96180SJosh Poimboeuf 		if (!next_insn) {
3788e7c0219bSPeter Zijlstra 			if (state.cfi.cfa.base == CFI_UNDEFINED)
378900d96180SJosh Poimboeuf 				return 0;
37906b023c78SJosh Poimboeuf 			if (file->ignore_unreachables)
37916b023c78SJosh Poimboeuf 				return 0;
37926b023c78SJosh Poimboeuf 
3793c5995abeSJosh Poimboeuf 			WARN("%s%sunexpected end of section %s",
3794188d90f8SJosh Poimboeuf 			     func ? func->name : "", func ? "(): " : "",
3795c5995abeSJosh Poimboeuf 			     sec->name);
3796dcc914f4SJosh Poimboeuf 			return 1;
3797dcc914f4SJosh Poimboeuf 		}
379800d96180SJosh Poimboeuf 
37998b946cc3SPeter Zijlstra 		prev_insn = insn;
380000d96180SJosh Poimboeuf 		insn = next_insn;
3801dcc914f4SJosh Poimboeuf 	}
3802dcc914f4SJosh Poimboeuf 
3803dcc914f4SJosh Poimboeuf 	return 0;
3804dcc914f4SJosh Poimboeuf }
3805dcc914f4SJosh Poimboeuf 
validate_unwind_hint(struct objtool_file * file,struct instruction * insn,struct insn_state * state)38061c34496eSPeter Zijlstra static int validate_unwind_hint(struct objtool_file *file,
38071c34496eSPeter Zijlstra 				  struct instruction *insn,
38081c34496eSPeter Zijlstra 				  struct insn_state *state)
38091c34496eSPeter Zijlstra {
3810c84301d7SJosh Poimboeuf 	if (insn->hint && !insn->visited) {
38111c34496eSPeter Zijlstra 		int ret = validate_branch(file, insn_func(insn), insn, *state);
3812ced23d2eSJosh Poimboeuf 		if (ret)
3813ced23d2eSJosh Poimboeuf 			BT_INSN(insn, "<=== (hint)");
38141c34496eSPeter Zijlstra 		return ret;
38151c34496eSPeter Zijlstra 	}
38161c34496eSPeter Zijlstra 
38171c34496eSPeter Zijlstra 	return 0;
38181c34496eSPeter Zijlstra }
38191c34496eSPeter Zijlstra 
validate_unwind_hints(struct objtool_file * file,struct section * sec)3820932f8e98SPeter Zijlstra static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
382139358a03SJosh Poimboeuf {
382239358a03SJosh Poimboeuf 	struct instruction *insn;
382339358a03SJosh Poimboeuf 	struct insn_state state;
38241c34496eSPeter Zijlstra 	int warnings = 0;
382539358a03SJosh Poimboeuf 
382639358a03SJosh Poimboeuf 	if (!file->hints)
382739358a03SJosh Poimboeuf 		return 0;
382839358a03SJosh Poimboeuf 
3829753da417SJosh Poimboeuf 	init_insn_state(file, &state, sec);
383039358a03SJosh Poimboeuf 
3831932f8e98SPeter Zijlstra 	if (sec) {
38321c34496eSPeter Zijlstra 		sec_for_each_insn(file, sec, insn)
38331c34496eSPeter Zijlstra 			warnings += validate_unwind_hint(file, insn, &state);
3834932f8e98SPeter Zijlstra 	} else {
38351c34496eSPeter Zijlstra 		for_each_insn(file, insn)
38361c34496eSPeter Zijlstra 			warnings += validate_unwind_hint(file, insn, &state);
383739358a03SJosh Poimboeuf 	}
383839358a03SJosh Poimboeuf 
383939358a03SJosh Poimboeuf 	return warnings;
384039358a03SJosh Poimboeuf }
384139358a03SJosh Poimboeuf 
3842a09a6e23SPeter Zijlstra /*
3843a09a6e23SPeter Zijlstra  * Validate rethunk entry constraint: must untrain RET before the first RET.
3844a09a6e23SPeter Zijlstra  *
38454708ea14SJosh Poimboeuf  * Follow every branch (intra-function) and ensure VALIDATE_UNRET_END comes
3846a09a6e23SPeter Zijlstra  * before an actual RET instruction.
3847a09a6e23SPeter Zijlstra  */
validate_unret(struct objtool_file * file,struct instruction * insn)38484708ea14SJosh Poimboeuf static int validate_unret(struct objtool_file *file, struct instruction *insn)
3849a09a6e23SPeter Zijlstra {
3850a09a6e23SPeter Zijlstra 	struct instruction *next, *dest;
3851d49d1666SLu Hongfei 	int ret;
3852a09a6e23SPeter Zijlstra 
3853a09a6e23SPeter Zijlstra 	for (;;) {
3854a09a6e23SPeter Zijlstra 		next = next_insn_to_validate(file, insn);
3855a09a6e23SPeter Zijlstra 
38564708ea14SJosh Poimboeuf 		if (insn->visited & VISITED_UNRET)
3857a09a6e23SPeter Zijlstra 			return 0;
3858a09a6e23SPeter Zijlstra 
38594708ea14SJosh Poimboeuf 		insn->visited |= VISITED_UNRET;
3860a09a6e23SPeter Zijlstra 
38611154bbd3SJosh Poimboeuf 		if (insn->alts) {
3862a09a6e23SPeter Zijlstra 			struct alternative *alt;
3863d5406654SPeter Zijlstra 			for (alt = insn->alts; alt; alt = alt->next) {
38644708ea14SJosh Poimboeuf 				ret = validate_unret(file, alt->insn);
3865a09a6e23SPeter Zijlstra 				if (ret) {
3866ced23d2eSJosh Poimboeuf 					BT_INSN(insn, "(alt)");
3867a09a6e23SPeter Zijlstra 					return ret;
3868a09a6e23SPeter Zijlstra 				}
3869a09a6e23SPeter Zijlstra 			}
3870a09a6e23SPeter Zijlstra 		}
3871a09a6e23SPeter Zijlstra 
3872a09a6e23SPeter Zijlstra 		switch (insn->type) {
3873a09a6e23SPeter Zijlstra 
3874a09a6e23SPeter Zijlstra 		case INSN_CALL_DYNAMIC:
3875a09a6e23SPeter Zijlstra 		case INSN_JUMP_DYNAMIC:
3876a09a6e23SPeter Zijlstra 		case INSN_JUMP_DYNAMIC_CONDITIONAL:
3877246b2c85SJosh Poimboeuf 			WARN_INSN(insn, "early indirect call");
3878a09a6e23SPeter Zijlstra 			return 1;
3879a09a6e23SPeter Zijlstra 
3880a09a6e23SPeter Zijlstra 		case INSN_JUMP_UNCONDITIONAL:
3881a09a6e23SPeter Zijlstra 		case INSN_JUMP_CONDITIONAL:
3882a09a6e23SPeter Zijlstra 			if (!is_sibling_call(insn)) {
3883a09a6e23SPeter Zijlstra 				if (!insn->jump_dest) {
3884246b2c85SJosh Poimboeuf 					WARN_INSN(insn, "unresolved jump target after linking?!?");
3885c5995abeSJosh Poimboeuf 					return 1;
3886a09a6e23SPeter Zijlstra 				}
38874708ea14SJosh Poimboeuf 				ret = validate_unret(file, insn->jump_dest);
3888a09a6e23SPeter Zijlstra 				if (ret) {
3889ced23d2eSJosh Poimboeuf 					BT_INSN(insn, "(branch%s)",
3890a09a6e23SPeter Zijlstra 						insn->type == INSN_JUMP_CONDITIONAL ? "-cond" : "");
3891a09a6e23SPeter Zijlstra 					return ret;
3892a09a6e23SPeter Zijlstra 				}
3893a09a6e23SPeter Zijlstra 
3894a09a6e23SPeter Zijlstra 				if (insn->type == INSN_JUMP_UNCONDITIONAL)
3895a09a6e23SPeter Zijlstra 					return 0;
3896a09a6e23SPeter Zijlstra 
3897a09a6e23SPeter Zijlstra 				break;
3898a09a6e23SPeter Zijlstra 			}
3899a09a6e23SPeter Zijlstra 
3900a09a6e23SPeter Zijlstra 			/* fallthrough */
3901a09a6e23SPeter Zijlstra 		case INSN_CALL:
3902c6f5dc28SPeter Zijlstra 			dest = find_insn(file, insn_call_dest(insn)->sec,
3903c6f5dc28SPeter Zijlstra 					 insn_call_dest(insn)->offset);
3904a09a6e23SPeter Zijlstra 			if (!dest) {
3905a09a6e23SPeter Zijlstra 				WARN("Unresolved function after linking!?: %s",
3906c6f5dc28SPeter Zijlstra 				     insn_call_dest(insn)->name);
3907c5995abeSJosh Poimboeuf 				return 1;
3908a09a6e23SPeter Zijlstra 			}
3909a09a6e23SPeter Zijlstra 
39104708ea14SJosh Poimboeuf 			ret = validate_unret(file, dest);
3911a09a6e23SPeter Zijlstra 			if (ret) {
3912ced23d2eSJosh Poimboeuf 				BT_INSN(insn, "(call)");
3913a09a6e23SPeter Zijlstra 				return ret;
3914a09a6e23SPeter Zijlstra 			}
3915a09a6e23SPeter Zijlstra 			/*
3916a09a6e23SPeter Zijlstra 			 * If a call returns without error, it must have seen UNTRAIN_RET.
3917a09a6e23SPeter Zijlstra 			 * Therefore any non-error return is a success.
3918a09a6e23SPeter Zijlstra 			 */
3919a09a6e23SPeter Zijlstra 			return 0;
3920a09a6e23SPeter Zijlstra 
3921a09a6e23SPeter Zijlstra 		case INSN_RETURN:
3922246b2c85SJosh Poimboeuf 			WARN_INSN(insn, "RET before UNTRAIN");
3923a09a6e23SPeter Zijlstra 			return 1;
3924a09a6e23SPeter Zijlstra 
3925fe1042b1SJosh Poimboeuf 		case INSN_SYSCALL:
3926a8df7d0eSJosh Poimboeuf 			break;
39272dbbca9bSJosh Poimboeuf 
39282dbbca9bSJosh Poimboeuf 		case INSN_SYSRET:
3929a8df7d0eSJosh Poimboeuf 			return 0;
3930a8df7d0eSJosh Poimboeuf 
3931a09a6e23SPeter Zijlstra 		case INSN_NOP:
3932a09a6e23SPeter Zijlstra 			if (insn->retpoline_safe)
3933a09a6e23SPeter Zijlstra 				return 0;
3934a09a6e23SPeter Zijlstra 			break;
3935a09a6e23SPeter Zijlstra 
3936a09a6e23SPeter Zijlstra 		default:
3937a09a6e23SPeter Zijlstra 			break;
3938a09a6e23SPeter Zijlstra 		}
3939a09a6e23SPeter Zijlstra 
39409f9cc012SJosh Poimboeuf 		if (insn->dead_end)
39419f9cc012SJosh Poimboeuf 			return 0;
39429f9cc012SJosh Poimboeuf 
3943a09a6e23SPeter Zijlstra 		if (!next) {
3944246b2c85SJosh Poimboeuf 			WARN_INSN(insn, "teh end!");
3945c5995abeSJosh Poimboeuf 			return 1;
3946a09a6e23SPeter Zijlstra 		}
3947a09a6e23SPeter Zijlstra 		insn = next;
3948a09a6e23SPeter Zijlstra 	}
3949a09a6e23SPeter Zijlstra 
3950d49d1666SLu Hongfei 	return 0;
3951a09a6e23SPeter Zijlstra }
3952a09a6e23SPeter Zijlstra 
3953a09a6e23SPeter Zijlstra /*
39544708ea14SJosh Poimboeuf  * Validate that all branches starting at VALIDATE_UNRET_BEGIN encounter
39554708ea14SJosh Poimboeuf  * VALIDATE_UNRET_END before RET.
3956a09a6e23SPeter Zijlstra  */
validate_unrets(struct objtool_file * file)39574708ea14SJosh Poimboeuf static int validate_unrets(struct objtool_file *file)
3958a09a6e23SPeter Zijlstra {
3959a09a6e23SPeter Zijlstra 	struct instruction *insn;
3960c5995abeSJosh Poimboeuf 	int warnings = 0;
3961a09a6e23SPeter Zijlstra 
3962a09a6e23SPeter Zijlstra 	for_each_insn(file, insn) {
39634708ea14SJosh Poimboeuf 		if (!insn->unret)
3964a09a6e23SPeter Zijlstra 			continue;
3965a09a6e23SPeter Zijlstra 
3966c5995abeSJosh Poimboeuf 		warnings += validate_unret(file, insn);
3967a09a6e23SPeter Zijlstra 	}
3968a09a6e23SPeter Zijlstra 
3969a09a6e23SPeter Zijlstra 	return warnings;
3970a09a6e23SPeter Zijlstra }
3971a09a6e23SPeter Zijlstra 
validate_retpoline(struct objtool_file * file)3972b5bc2231SPeter Zijlstra static int validate_retpoline(struct objtool_file *file)
3973b5bc2231SPeter Zijlstra {
3974b5bc2231SPeter Zijlstra 	struct instruction *insn;
3975b5bc2231SPeter Zijlstra 	int warnings = 0;
3976b5bc2231SPeter Zijlstra 
3977b5bc2231SPeter Zijlstra 	for_each_insn(file, insn) {
3978b5bc2231SPeter Zijlstra 		if (insn->type != INSN_JUMP_DYNAMIC &&
39799bb2ec60SPeter Zijlstra 		    insn->type != INSN_CALL_DYNAMIC &&
39809bb2ec60SPeter Zijlstra 		    insn->type != INSN_RETURN)
3981b5bc2231SPeter Zijlstra 			continue;
3982b5bc2231SPeter Zijlstra 
3983b5bc2231SPeter Zijlstra 		if (insn->retpoline_safe)
3984b5bc2231SPeter Zijlstra 			continue;
3985b5bc2231SPeter Zijlstra 
39866644ee84SPeter Zijlstra 		if (insn->sec->init)
3987ca41b97eSPeter Zijlstra 			continue;
3988ca41b97eSPeter Zijlstra 
39899bb2ec60SPeter Zijlstra 		if (insn->type == INSN_RETURN) {
3990f43b9876SPeter Zijlstra 			if (opts.rethunk) {
39910911b8c5SBreno Leitao 				WARN_INSN(insn, "'naked' return found in MITIGATION_RETHUNK build");
3992c5995abeSJosh Poimboeuf 				warnings++;
3993c5995abeSJosh Poimboeuf 			}
3994f43b9876SPeter Zijlstra 			continue;
39959bb2ec60SPeter Zijlstra 		}
3996b5bc2231SPeter Zijlstra 
3997c5995abeSJosh Poimboeuf 		WARN_INSN(insn, "indirect %s found in MITIGATION_RETPOLINE build",
3998c5995abeSJosh Poimboeuf 			  insn->type == INSN_JUMP_DYNAMIC ? "jump" : "call");
3999b5bc2231SPeter Zijlstra 		warnings++;
4000b5bc2231SPeter Zijlstra 	}
4001b5bc2231SPeter Zijlstra 
4002b5bc2231SPeter Zijlstra 	return warnings;
4003b5bc2231SPeter Zijlstra }
4004b5bc2231SPeter Zijlstra 
is_kasan_insn(struct instruction * insn)4005dcc914f4SJosh Poimboeuf static bool is_kasan_insn(struct instruction *insn)
4006dcc914f4SJosh Poimboeuf {
4007dcc914f4SJosh Poimboeuf 	return (insn->type == INSN_CALL &&
4008c6f5dc28SPeter Zijlstra 		!strcmp(insn_call_dest(insn)->name, "__asan_handle_no_return"));
4009dcc914f4SJosh Poimboeuf }
4010dcc914f4SJosh Poimboeuf 
is_ubsan_insn(struct instruction * insn)4011dcc914f4SJosh Poimboeuf static bool is_ubsan_insn(struct instruction *insn)
4012dcc914f4SJosh Poimboeuf {
4013dcc914f4SJosh Poimboeuf 	return (insn->type == INSN_CALL &&
4014c6f5dc28SPeter Zijlstra 		!strcmp(insn_call_dest(insn)->name,
4015dcc914f4SJosh Poimboeuf 			"__ubsan_handle_builtin_unreachable"));
4016dcc914f4SJosh Poimboeuf }
4017dcc914f4SJosh Poimboeuf 
ignore_unreachable_insn(struct objtool_file * file,struct instruction * insn)401814db1f0aSIlie Halip static bool ignore_unreachable_insn(struct objtool_file *file, struct instruction *insn)
4019dcc914f4SJosh Poimboeuf {
4020c84301d7SJosh Poimboeuf 	struct symbol *func = insn_func(insn);
402114db1f0aSIlie Halip 	struct instruction *prev_insn;
4022c84301d7SJosh Poimboeuf 	int i;
4023dcc914f4SJosh Poimboeuf 
40241154bbd3SJosh Poimboeuf 	if (insn->type == INSN_NOP || insn->type == INSN_TRAP || (func && func->ignore))
4025baa41469SJosh Poimboeuf 		return true;
4026baa41469SJosh Poimboeuf 
4027baa41469SJosh Poimboeuf 	/*
402882a8954aSPeter Zijlstra 	 * Ignore alternative replacement instructions.  This can happen
40290e2bb2bcSJosh Poimboeuf 	 * when a whitelisted function uses one of the ALTERNATIVE macros.
4030baa41469SJosh Poimboeuf 	 */
403182a8954aSPeter Zijlstra 	if (!strcmp(insn->sec->name, ".altinstr_replacement") ||
40320e2bb2bcSJosh Poimboeuf 	    !strcmp(insn->sec->name, ".altinstr_aux"))
4033dcc914f4SJosh Poimboeuf 		return true;
4034dcc914f4SJosh Poimboeuf 
40354adb2368SPeter Zijlstra 	/*
4036753da417SJosh Poimboeuf 	 * Whole archive runs might encounter dead code from weak symbols.
40374adb2368SPeter Zijlstra 	 * This is where the linker will have dropped the weak symbol in
40384adb2368SPeter Zijlstra 	 * favour of a regular symbol, but leaves the code in place.
40394adb2368SPeter Zijlstra 	 *
40404adb2368SPeter Zijlstra 	 * In this case we'll find a piece of code (whole function) that is not
40414adb2368SPeter Zijlstra 	 * covered by a !section symbol. Ignore them.
40424adb2368SPeter Zijlstra 	 */
4043c84301d7SJosh Poimboeuf 	if (opts.link && !func) {
40444adb2368SPeter Zijlstra 		int size = find_symbol_hole_containing(insn->sec, insn->offset);
40454adb2368SPeter Zijlstra 		unsigned long end = insn->offset + size;
40464adb2368SPeter Zijlstra 
40474adb2368SPeter Zijlstra 		if (!size) /* not a hole */
40484adb2368SPeter Zijlstra 			return false;
40494adb2368SPeter Zijlstra 
40504adb2368SPeter Zijlstra 		if (size < 0) /* hole until the end */
40514adb2368SPeter Zijlstra 			return true;
40524adb2368SPeter Zijlstra 
40534adb2368SPeter Zijlstra 		sec_for_each_insn_continue(file, insn) {
40544adb2368SPeter Zijlstra 			/*
40554adb2368SPeter Zijlstra 			 * If we reach a visited instruction at or before the
40564adb2368SPeter Zijlstra 			 * end of the hole, ignore the unreachable.
40574adb2368SPeter Zijlstra 			 */
40584adb2368SPeter Zijlstra 			if (insn->visited)
40594adb2368SPeter Zijlstra 				return true;
40604adb2368SPeter Zijlstra 
40614adb2368SPeter Zijlstra 			if (insn->offset >= end)
40624adb2368SPeter Zijlstra 				break;
40634adb2368SPeter Zijlstra 
40644adb2368SPeter Zijlstra 			/*
40654adb2368SPeter Zijlstra 			 * If this hole jumps to a .cold function, mark it ignore too.
40664adb2368SPeter Zijlstra 			 */
4067dbcdbdfdSPeter Zijlstra 			if (insn->jump_dest && insn_func(insn->jump_dest) &&
4068dbcdbdfdSPeter Zijlstra 			    strstr(insn_func(insn->jump_dest)->name, ".cold")) {
4069c84301d7SJosh Poimboeuf 				insn_func(insn->jump_dest)->ignore = true;
40704adb2368SPeter Zijlstra 			}
40714adb2368SPeter Zijlstra 		}
40724adb2368SPeter Zijlstra 
40734adb2368SPeter Zijlstra 		return false;
40744adb2368SPeter Zijlstra 	}
40754adb2368SPeter Zijlstra 
4076c84301d7SJosh Poimboeuf 	if (!func)
4077bd841d61SJosh Poimboeuf 		return false;
4078bd841d61SJosh Poimboeuf 
4079c84301d7SJosh Poimboeuf 	if (func->static_call_tramp)
40802105a927SPeter Zijlstra 		return true;
40812105a927SPeter Zijlstra 
4082bd841d61SJosh Poimboeuf 	/*
4083bd841d61SJosh Poimboeuf 	 * CONFIG_UBSAN_TRAP inserts a UD2 when it sees
4084bd841d61SJosh Poimboeuf 	 * __builtin_unreachable().  The BUG() macro has an unreachable() after
4085bd841d61SJosh Poimboeuf 	 * the UD2, which causes GCC's undefined trap logic to emit another UD2
4086bd841d61SJosh Poimboeuf 	 * (or occasionally a JMP to UD2).
408714db1f0aSIlie Halip 	 *
408814db1f0aSIlie Halip 	 * It may also insert a UD2 after calling a __noreturn function.
4089bd841d61SJosh Poimboeuf 	 */
40901c34496eSPeter Zijlstra 	prev_insn = prev_insn_same_sec(file, insn);
409169d41d6dSJosh Poimboeuf 	if (prev_insn && prev_insn->dead_end &&
4092bd841d61SJosh Poimboeuf 	    (insn->type == INSN_BUG ||
4093bd841d61SJosh Poimboeuf 	     (insn->type == INSN_JUMP_UNCONDITIONAL &&
4094bd841d61SJosh Poimboeuf 	      insn->jump_dest && insn->jump_dest->type == INSN_BUG)))
4095bd841d61SJosh Poimboeuf 		return true;
4096bd841d61SJosh Poimboeuf 
4097dcc914f4SJosh Poimboeuf 	/*
4098dcc914f4SJosh Poimboeuf 	 * Check if this (or a subsequent) instruction is related to
4099dcc914f4SJosh Poimboeuf 	 * CONFIG_UBSAN or CONFIG_KASAN.
4100dcc914f4SJosh Poimboeuf 	 *
4101dcc914f4SJosh Poimboeuf 	 * End the search at 5 instructions to avoid going into the weeds.
4102dcc914f4SJosh Poimboeuf 	 */
4103dcc914f4SJosh Poimboeuf 	for (i = 0; i < 5; i++) {
4104dcc914f4SJosh Poimboeuf 
4105dcc914f4SJosh Poimboeuf 		if (is_kasan_insn(insn) || is_ubsan_insn(insn))
4106dcc914f4SJosh Poimboeuf 			return true;
4107dcc914f4SJosh Poimboeuf 
4108fe24e271SJosh Poimboeuf 		if (insn->type == INSN_JUMP_UNCONDITIONAL) {
4109fe24e271SJosh Poimboeuf 			if (insn->jump_dest &&
4110c84301d7SJosh Poimboeuf 			    insn_func(insn->jump_dest) == func) {
4111dcc914f4SJosh Poimboeuf 				insn = insn->jump_dest;
4112dcc914f4SJosh Poimboeuf 				continue;
4113dcc914f4SJosh Poimboeuf 			}
4114dcc914f4SJosh Poimboeuf 
4115fe24e271SJosh Poimboeuf 			break;
4116fe24e271SJosh Poimboeuf 		}
4117fe24e271SJosh Poimboeuf 
4118c84301d7SJosh Poimboeuf 		if (insn->offset + insn->len >= func->offset + func->len)
4119dcc914f4SJosh Poimboeuf 			break;
4120fe24e271SJosh Poimboeuf 
41211c34496eSPeter Zijlstra 		insn = next_insn_same_sec(file, insn);
4122dcc914f4SJosh Poimboeuf 	}
4123dcc914f4SJosh Poimboeuf 
4124dcc914f4SJosh Poimboeuf 	return false;
4125dcc914f4SJosh Poimboeuf }
4126dcc914f4SJosh Poimboeuf 
add_prefix_symbol(struct objtool_file * file,struct symbol * func)4127bd456a1bSJosh Poimboeuf static int add_prefix_symbol(struct objtool_file *file, struct symbol *func)
41289f2899feSPeter Zijlstra {
4129bd456a1bSJosh Poimboeuf 	struct instruction *insn, *prev;
41305743654fSJosh Poimboeuf 	struct cfi_state *cfi;
41319f2899feSPeter Zijlstra 
4132bd456a1bSJosh Poimboeuf 	insn = find_insn(file, func->sec, func->offset);
4133bd456a1bSJosh Poimboeuf 	if (!insn)
4134bd456a1bSJosh Poimboeuf 		return -1;
4135bd456a1bSJosh Poimboeuf 
4136bd456a1bSJosh Poimboeuf 	for (prev = prev_insn_same_sec(file, insn);
4137bd456a1bSJosh Poimboeuf 	     prev;
4138bd456a1bSJosh Poimboeuf 	     prev = prev_insn_same_sec(file, prev)) {
41399f2899feSPeter Zijlstra 		u64 offset;
41409f2899feSPeter Zijlstra 
41419f2899feSPeter Zijlstra 		if (prev->type != INSN_NOP)
4142bd456a1bSJosh Poimboeuf 			return -1;
41439f2899feSPeter Zijlstra 
41449f2899feSPeter Zijlstra 		offset = func->offset - prev->offset;
4145bd456a1bSJosh Poimboeuf 
4146bd456a1bSJosh Poimboeuf 		if (offset > opts.prefix)
4147bd456a1bSJosh Poimboeuf 			return -1;
4148bd456a1bSJosh Poimboeuf 
4149bd456a1bSJosh Poimboeuf 		if (offset < opts.prefix)
4150bd456a1bSJosh Poimboeuf 			continue;
4151bd456a1bSJosh Poimboeuf 
41529f2899feSPeter Zijlstra 		elf_create_prefix_symbol(file->elf, func, opts.prefix);
41539f2899feSPeter Zijlstra 		break;
41549f2899feSPeter Zijlstra 	}
4155bd456a1bSJosh Poimboeuf 
4156bd456a1bSJosh Poimboeuf 	if (!prev)
4157bd456a1bSJosh Poimboeuf 		return -1;
41589f2899feSPeter Zijlstra 
41595743654fSJosh Poimboeuf 	if (!insn->cfi) {
41605743654fSJosh Poimboeuf 		/*
41615743654fSJosh Poimboeuf 		 * This can happen if stack validation isn't enabled or the
41625743654fSJosh Poimboeuf 		 * function is annotated with STACK_FRAME_NON_STANDARD.
41635743654fSJosh Poimboeuf 		 */
41645743654fSJosh Poimboeuf 		return 0;
41659f2899feSPeter Zijlstra 	}
41669f2899feSPeter Zijlstra 
41675743654fSJosh Poimboeuf 	/* Propagate insn->cfi to the prefix code */
41685743654fSJosh Poimboeuf 	cfi = cfi_hash_find_or_add(insn->cfi);
41695743654fSJosh Poimboeuf 	for (; prev != insn; prev = next_insn_same_sec(file, prev))
41705743654fSJosh Poimboeuf 		prev->cfi = cfi;
41715743654fSJosh Poimboeuf 
41729f2899feSPeter Zijlstra 	return 0;
41739f2899feSPeter Zijlstra }
41749f2899feSPeter Zijlstra 
add_prefix_symbols(struct objtool_file * file)4175bd456a1bSJosh Poimboeuf static int add_prefix_symbols(struct objtool_file *file)
4176bd456a1bSJosh Poimboeuf {
4177bd456a1bSJosh Poimboeuf 	struct section *sec;
4178bd456a1bSJosh Poimboeuf 	struct symbol *func;
4179bd456a1bSJosh Poimboeuf 
4180bd456a1bSJosh Poimboeuf 	for_each_sec(file, sec) {
4181bd456a1bSJosh Poimboeuf 		if (!(sec->sh.sh_flags & SHF_EXECINSTR))
4182bd456a1bSJosh Poimboeuf 			continue;
4183bd456a1bSJosh Poimboeuf 
4184bd456a1bSJosh Poimboeuf 		sec_for_each_sym(sec, func) {
4185bd456a1bSJosh Poimboeuf 			if (func->type != STT_FUNC)
4186bd456a1bSJosh Poimboeuf 				continue;
4187bd456a1bSJosh Poimboeuf 
4188bd456a1bSJosh Poimboeuf 			add_prefix_symbol(file, func);
4189bd456a1bSJosh Poimboeuf 		}
4190bd456a1bSJosh Poimboeuf 	}
4191bd456a1bSJosh Poimboeuf 
4192d49d1666SLu Hongfei 	return 0;
4193bd456a1bSJosh Poimboeuf }
4194bd456a1bSJosh Poimboeuf 
validate_symbol(struct objtool_file * file,struct section * sec,struct symbol * sym,struct insn_state * state)41954b5e2e7fSPeter Zijlstra static int validate_symbol(struct objtool_file *file, struct section *sec,
41964b5e2e7fSPeter Zijlstra 			   struct symbol *sym, struct insn_state *state)
41974b5e2e7fSPeter Zijlstra {
41984b5e2e7fSPeter Zijlstra 	struct instruction *insn;
41994b5e2e7fSPeter Zijlstra 	int ret;
42004b5e2e7fSPeter Zijlstra 
42014b5e2e7fSPeter Zijlstra 	if (!sym->len) {
42024b5e2e7fSPeter Zijlstra 		WARN("%s() is missing an ELF size annotation", sym->name);
42034b5e2e7fSPeter Zijlstra 		return 1;
42044b5e2e7fSPeter Zijlstra 	}
42054b5e2e7fSPeter Zijlstra 
42064b5e2e7fSPeter Zijlstra 	if (sym->pfunc != sym || sym->alias != sym)
42074b5e2e7fSPeter Zijlstra 		return 0;
42084b5e2e7fSPeter Zijlstra 
42094b5e2e7fSPeter Zijlstra 	insn = find_insn(file, sec, sym->offset);
4210c84301d7SJosh Poimboeuf 	if (!insn || insn->visited)
42114b5e2e7fSPeter Zijlstra 		return 0;
42124b5e2e7fSPeter Zijlstra 
4213e1a9dda7SJosh Poimboeuf 	if (opts.uaccess)
42144b5e2e7fSPeter Zijlstra 		state->uaccess = sym->uaccess_safe;
42154b5e2e7fSPeter Zijlstra 
4216dbcdbdfdSPeter Zijlstra 	ret = validate_branch(file, insn_func(insn), insn, *state);
4217ced23d2eSJosh Poimboeuf 	if (ret)
4218ced23d2eSJosh Poimboeuf 		BT_INSN(insn, "<=== (sym)");
42194b5e2e7fSPeter Zijlstra 	return ret;
42204b5e2e7fSPeter Zijlstra }
42214b5e2e7fSPeter Zijlstra 
validate_section(struct objtool_file * file,struct section * sec)4222350994bfSPeter Zijlstra static int validate_section(struct objtool_file *file, struct section *sec)
4223dcc914f4SJosh Poimboeuf {
4224baa41469SJosh Poimboeuf 	struct insn_state state;
42254b5e2e7fSPeter Zijlstra 	struct symbol *func;
42264b5e2e7fSPeter Zijlstra 	int warnings = 0;
4227dcc914f4SJosh Poimboeuf 
42289290e772SJosh Poimboeuf 	sec_for_each_sym(sec, func) {
4229e10cd8feSJosh Poimboeuf 		if (func->type != STT_FUNC)
4230e10cd8feSJosh Poimboeuf 			continue;
4231e10cd8feSJosh Poimboeuf 
4232753da417SJosh Poimboeuf 		init_insn_state(file, &state, sec);
4233b735bd3eSJosh Poimboeuf 		set_func_state(&state.cfi);
42340699e551SJulien Thierry 
42354b5e2e7fSPeter Zijlstra 		warnings += validate_symbol(file, sec, func, &state);
4236dcc914f4SJosh Poimboeuf 	}
4237350994bfSPeter Zijlstra 
4238350994bfSPeter Zijlstra 	return warnings;
4239dcc914f4SJosh Poimboeuf }
4240dcc914f4SJosh Poimboeuf 
validate_noinstr_sections(struct objtool_file * file)4241753da417SJosh Poimboeuf static int validate_noinstr_sections(struct objtool_file *file)
4242c4a33939SPeter Zijlstra {
4243c4a33939SPeter Zijlstra 	struct section *sec;
4244932f8e98SPeter Zijlstra 	int warnings = 0;
4245c4a33939SPeter Zijlstra 
4246c4a33939SPeter Zijlstra 	sec = find_section_by_name(file->elf, ".noinstr.text");
42470cc9ac8dSThomas Gleixner 	if (sec) {
4248932f8e98SPeter Zijlstra 		warnings += validate_section(file, sec);
4249932f8e98SPeter Zijlstra 		warnings += validate_unwind_hints(file, sec);
42500cc9ac8dSThomas Gleixner 	}
42510cc9ac8dSThomas Gleixner 
42520cc9ac8dSThomas Gleixner 	sec = find_section_by_name(file->elf, ".entry.text");
42530cc9ac8dSThomas Gleixner 	if (sec) {
42540cc9ac8dSThomas Gleixner 		warnings += validate_section(file, sec);
42550cc9ac8dSThomas Gleixner 		warnings += validate_unwind_hints(file, sec);
42560cc9ac8dSThomas Gleixner 	}
4257932f8e98SPeter Zijlstra 
42582b5a0e42SPeter Zijlstra 	sec = find_section_by_name(file->elf, ".cpuidle.text");
42592b5a0e42SPeter Zijlstra 	if (sec) {
42602b5a0e42SPeter Zijlstra 		warnings += validate_section(file, sec);
42612b5a0e42SPeter Zijlstra 		warnings += validate_unwind_hints(file, sec);
42622b5a0e42SPeter Zijlstra 	}
42632b5a0e42SPeter Zijlstra 
4264932f8e98SPeter Zijlstra 	return warnings;
4265c4a33939SPeter Zijlstra }
4266c4a33939SPeter Zijlstra 
validate_functions(struct objtool_file * file)4267350994bfSPeter Zijlstra static int validate_functions(struct objtool_file *file)
4268350994bfSPeter Zijlstra {
4269350994bfSPeter Zijlstra 	struct section *sec;
4270350994bfSPeter Zijlstra 	int warnings = 0;
4271350994bfSPeter Zijlstra 
4272da837bd6SPeter Zijlstra 	for_each_sec(file, sec) {
4273da837bd6SPeter Zijlstra 		if (!(sec->sh.sh_flags & SHF_EXECINSTR))
4274da837bd6SPeter Zijlstra 			continue;
4275da837bd6SPeter Zijlstra 
4276350994bfSPeter Zijlstra 		warnings += validate_section(file, sec);
4277da837bd6SPeter Zijlstra 	}
4278350994bfSPeter Zijlstra 
4279baa41469SJosh Poimboeuf 	return warnings;
4280baa41469SJosh Poimboeuf }
4281dcc914f4SJosh Poimboeuf 
mark_endbr_used(struct instruction * insn)42823c6f9f77SJosh Poimboeuf static void mark_endbr_used(struct instruction *insn)
42833c6f9f77SJosh Poimboeuf {
42843c6f9f77SJosh Poimboeuf 	if (!list_empty(&insn->call_node))
42853c6f9f77SJosh Poimboeuf 		list_del_init(&insn->call_node);
42863c6f9f77SJosh Poimboeuf }
42873c6f9f77SJosh Poimboeuf 
noendbr_range(struct objtool_file * file,struct instruction * insn)428808ef8c40SPeter Zijlstra static bool noendbr_range(struct objtool_file *file, struct instruction *insn)
428908ef8c40SPeter Zijlstra {
429008ef8c40SPeter Zijlstra 	struct symbol *sym = find_symbol_containing(insn->sec, insn->offset-1);
429108ef8c40SPeter Zijlstra 	struct instruction *first;
429208ef8c40SPeter Zijlstra 
429308ef8c40SPeter Zijlstra 	if (!sym)
429408ef8c40SPeter Zijlstra 		return false;
429508ef8c40SPeter Zijlstra 
429608ef8c40SPeter Zijlstra 	first = find_insn(file, sym->sec, sym->offset);
429708ef8c40SPeter Zijlstra 	if (!first)
429808ef8c40SPeter Zijlstra 		return false;
429908ef8c40SPeter Zijlstra 
430008ef8c40SPeter Zijlstra 	if (first->type != INSN_ENDBR && !first->noendbr)
430108ef8c40SPeter Zijlstra 		return false;
430208ef8c40SPeter Zijlstra 
430308ef8c40SPeter Zijlstra 	return insn->offset == sym->offset + sym->len;
430408ef8c40SPeter Zijlstra }
430508ef8c40SPeter Zijlstra 
__validate_ibt_insn(struct objtool_file * file,struct instruction * insn,struct instruction * dest)4306ed1cb76eSJosh Poimboeuf static int __validate_ibt_insn(struct objtool_file *file, struct instruction *insn,
4307ed1cb76eSJosh Poimboeuf 			       struct instruction *dest)
43083c6f9f77SJosh Poimboeuf {
43093c6f9f77SJosh Poimboeuf 	if (dest->type == INSN_ENDBR) {
43103c6f9f77SJosh Poimboeuf 		mark_endbr_used(dest);
4311ed1cb76eSJosh Poimboeuf 		return 0;
43123c6f9f77SJosh Poimboeuf 	}
43133c6f9f77SJosh Poimboeuf 
431472178d5dSJosh Poimboeuf 	if (insn_func(dest) && insn_func(insn) &&
431572178d5dSJosh Poimboeuf 	    insn_func(dest)->pfunc == insn_func(insn)->pfunc) {
43163c6f9f77SJosh Poimboeuf 		/*
43173c6f9f77SJosh Poimboeuf 		 * Anything from->to self is either _THIS_IP_ or
43183c6f9f77SJosh Poimboeuf 		 * IRET-to-self.
43193c6f9f77SJosh Poimboeuf 		 *
43203c6f9f77SJosh Poimboeuf 		 * There is no sane way to annotate _THIS_IP_ since the
43213c6f9f77SJosh Poimboeuf 		 * compiler treats the relocation as a constant and is
43223c6f9f77SJosh Poimboeuf 		 * happy to fold in offsets, skewing any annotation we
43233c6f9f77SJosh Poimboeuf 		 * do, leading to vast amounts of false-positives.
43243c6f9f77SJosh Poimboeuf 		 *
43253c6f9f77SJosh Poimboeuf 		 * There's also compiler generated _THIS_IP_ through
43263c6f9f77SJosh Poimboeuf 		 * KCOV and such which we have no hope of annotating.
43273c6f9f77SJosh Poimboeuf 		 *
43283c6f9f77SJosh Poimboeuf 		 * As such, blanket accept self-references without
43293c6f9f77SJosh Poimboeuf 		 * issue.
43303c6f9f77SJosh Poimboeuf 		 */
4331ed1cb76eSJosh Poimboeuf 		return 0;
43323c6f9f77SJosh Poimboeuf 	}
43333c6f9f77SJosh Poimboeuf 
433408ef8c40SPeter Zijlstra 	/*
433508ef8c40SPeter Zijlstra 	 * Accept anything ANNOTATE_NOENDBR.
433608ef8c40SPeter Zijlstra 	 */
43373c6f9f77SJosh Poimboeuf 	if (dest->noendbr)
4338ed1cb76eSJosh Poimboeuf 		return 0;
43393c6f9f77SJosh Poimboeuf 
434008ef8c40SPeter Zijlstra 	/*
434108ef8c40SPeter Zijlstra 	 * Accept if this is the instruction after a symbol
434208ef8c40SPeter Zijlstra 	 * that is (no)endbr -- typical code-range usage.
434308ef8c40SPeter Zijlstra 	 */
434408ef8c40SPeter Zijlstra 	if (noendbr_range(file, dest))
4345ed1cb76eSJosh Poimboeuf 		return 0;
434608ef8c40SPeter Zijlstra 
4347246b2c85SJosh Poimboeuf 	WARN_INSN(insn, "relocation to !ENDBR: %s", offstr(dest->sec, dest->offset));
4348ed1cb76eSJosh Poimboeuf 	return 1;
4349ed1cb76eSJosh Poimboeuf }
43503c6f9f77SJosh Poimboeuf 
validate_ibt_insn(struct objtool_file * file,struct instruction * insn)4351ed1cb76eSJosh Poimboeuf static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn)
4352ed1cb76eSJosh Poimboeuf {
4353ed1cb76eSJosh Poimboeuf 	struct instruction *dest;
4354ed1cb76eSJosh Poimboeuf 	struct reloc *reloc;
4355ed1cb76eSJosh Poimboeuf 	unsigned long off;
4356ed1cb76eSJosh Poimboeuf 	int warnings = 0;
4357ed1cb76eSJosh Poimboeuf 
4358ed1cb76eSJosh Poimboeuf 	/*
4359ed1cb76eSJosh Poimboeuf 	 * Looking for function pointer load relocations.  Ignore
4360ed1cb76eSJosh Poimboeuf 	 * direct/indirect branches:
4361ed1cb76eSJosh Poimboeuf 	 */
4362ed1cb76eSJosh Poimboeuf 	switch (insn->type) {
4363ed1cb76eSJosh Poimboeuf 
4364ed1cb76eSJosh Poimboeuf 	case INSN_CALL:
4365ed1cb76eSJosh Poimboeuf 	case INSN_CALL_DYNAMIC:
4366ed1cb76eSJosh Poimboeuf 	case INSN_JUMP_CONDITIONAL:
4367ed1cb76eSJosh Poimboeuf 	case INSN_JUMP_UNCONDITIONAL:
4368ed1cb76eSJosh Poimboeuf 	case INSN_JUMP_DYNAMIC:
4369ed1cb76eSJosh Poimboeuf 	case INSN_JUMP_DYNAMIC_CONDITIONAL:
4370ed1cb76eSJosh Poimboeuf 	case INSN_RETURN:
4371ed1cb76eSJosh Poimboeuf 	case INSN_NOP:
4372ed1cb76eSJosh Poimboeuf 		return 0;
4373ed1cb76eSJosh Poimboeuf 
4374ed1cb76eSJosh Poimboeuf 	case INSN_LEA_RIP:
4375ed1cb76eSJosh Poimboeuf 		if (!insn_reloc(file, insn)) {
4376ed1cb76eSJosh Poimboeuf 			/* local function pointer reference without reloc */
4377ed1cb76eSJosh Poimboeuf 
4378ed1cb76eSJosh Poimboeuf 			off = arch_jump_destination(insn);
4379ed1cb76eSJosh Poimboeuf 
4380ed1cb76eSJosh Poimboeuf 			dest = find_insn(file, insn->sec, off);
4381ed1cb76eSJosh Poimboeuf 			if (!dest) {
4382ed1cb76eSJosh Poimboeuf 				WARN_INSN(insn, "corrupt function pointer reference");
4383ed1cb76eSJosh Poimboeuf 				return 1;
4384ed1cb76eSJosh Poimboeuf 			}
4385ed1cb76eSJosh Poimboeuf 
4386ed1cb76eSJosh Poimboeuf 			return __validate_ibt_insn(file, insn, dest);
4387ed1cb76eSJosh Poimboeuf 		}
4388ed1cb76eSJosh Poimboeuf 		break;
4389ed1cb76eSJosh Poimboeuf 
4390ed1cb76eSJosh Poimboeuf 	default:
4391ed1cb76eSJosh Poimboeuf 		break;
4392ed1cb76eSJosh Poimboeuf 	}
4393ed1cb76eSJosh Poimboeuf 
4394ed1cb76eSJosh Poimboeuf 	for (reloc = insn_reloc(file, insn);
4395ed1cb76eSJosh Poimboeuf 	     reloc;
4396ed1cb76eSJosh Poimboeuf 	     reloc = find_reloc_by_dest_range(file->elf, insn->sec,
4397ed1cb76eSJosh Poimboeuf 					      reloc_offset(reloc) + 1,
4398ed1cb76eSJosh Poimboeuf 					      (insn->offset + insn->len) - (reloc_offset(reloc) + 1))) {
4399ed1cb76eSJosh Poimboeuf 
4400ed1cb76eSJosh Poimboeuf 		off = reloc->sym->offset;
4401ed1cb76eSJosh Poimboeuf 		if (reloc_type(reloc) == R_X86_64_PC32 ||
4402ed1cb76eSJosh Poimboeuf 		    reloc_type(reloc) == R_X86_64_PLT32)
4403ed1cb76eSJosh Poimboeuf 			off += arch_dest_reloc_offset(reloc_addend(reloc));
4404ed1cb76eSJosh Poimboeuf 		else
4405ed1cb76eSJosh Poimboeuf 			off += reloc_addend(reloc);
4406ed1cb76eSJosh Poimboeuf 
4407ed1cb76eSJosh Poimboeuf 		dest = find_insn(file, reloc->sym->sec, off);
4408ed1cb76eSJosh Poimboeuf 		if (!dest)
4409ed1cb76eSJosh Poimboeuf 			continue;
4410ed1cb76eSJosh Poimboeuf 
4411ed1cb76eSJosh Poimboeuf 		warnings += __validate_ibt_insn(file, insn, dest);
44123c6f9f77SJosh Poimboeuf 	}
44133c6f9f77SJosh Poimboeuf 
44143c6f9f77SJosh Poimboeuf 	return warnings;
44153c6f9f77SJosh Poimboeuf }
44163c6f9f77SJosh Poimboeuf 
validate_ibt_data_reloc(struct objtool_file * file,struct reloc * reloc)44173c6f9f77SJosh Poimboeuf static int validate_ibt_data_reloc(struct objtool_file *file,
44183c6f9f77SJosh Poimboeuf 				   struct reloc *reloc)
44193c6f9f77SJosh Poimboeuf {
44203c6f9f77SJosh Poimboeuf 	struct instruction *dest;
44213c6f9f77SJosh Poimboeuf 
44223c6f9f77SJosh Poimboeuf 	dest = find_insn(file, reloc->sym->sec,
44230696b6e3SJosh Poimboeuf 			 reloc->sym->offset + reloc_addend(reloc));
44243c6f9f77SJosh Poimboeuf 	if (!dest)
44253c6f9f77SJosh Poimboeuf 		return 0;
44263c6f9f77SJosh Poimboeuf 
44273c6f9f77SJosh Poimboeuf 	if (dest->type == INSN_ENDBR) {
44283c6f9f77SJosh Poimboeuf 		mark_endbr_used(dest);
44293c6f9f77SJosh Poimboeuf 		return 0;
44303c6f9f77SJosh Poimboeuf 	}
44313c6f9f77SJosh Poimboeuf 
44323c6f9f77SJosh Poimboeuf 	if (dest->noendbr)
44333c6f9f77SJosh Poimboeuf 		return 0;
44343c6f9f77SJosh Poimboeuf 
44353e7be635SJosh Poimboeuf 	WARN_FUNC(reloc->sec->base, reloc_offset(reloc),
44363e7be635SJosh Poimboeuf 		  "data relocation to !ENDBR: %s", offstr(dest->sec, dest->offset));
44373c6f9f77SJosh Poimboeuf 
44383c6f9f77SJosh Poimboeuf 	return 1;
44393c6f9f77SJosh Poimboeuf }
44403c6f9f77SJosh Poimboeuf 
44413c6f9f77SJosh Poimboeuf /*
44423c6f9f77SJosh Poimboeuf  * Validate IBT rules and remove used ENDBR instructions from the seal list.
44433c6f9f77SJosh Poimboeuf  * Unused ENDBR instructions will be annotated for sealing (i.e., replaced with
44443c6f9f77SJosh Poimboeuf  * NOPs) later, in create_ibt_endbr_seal_sections().
44453c6f9f77SJosh Poimboeuf  */
validate_ibt(struct objtool_file * file)444608f87a93SPeter Zijlstra static int validate_ibt(struct objtool_file *file)
444708f87a93SPeter Zijlstra {
444808f87a93SPeter Zijlstra 	struct section *sec;
444908f87a93SPeter Zijlstra 	struct reloc *reloc;
44503c6f9f77SJosh Poimboeuf 	struct instruction *insn;
44513c6f9f77SJosh Poimboeuf 	int warnings = 0;
44523c6f9f77SJosh Poimboeuf 
44533c6f9f77SJosh Poimboeuf 	for_each_insn(file, insn)
44543c6f9f77SJosh Poimboeuf 		warnings += validate_ibt_insn(file, insn);
445508f87a93SPeter Zijlstra 
445608f87a93SPeter Zijlstra 	for_each_sec(file, sec) {
445708f87a93SPeter Zijlstra 
44583c6f9f77SJosh Poimboeuf 		/* Already done by validate_ibt_insn() */
445908f87a93SPeter Zijlstra 		if (sec->sh.sh_flags & SHF_EXECINSTR)
446008f87a93SPeter Zijlstra 			continue;
446108f87a93SPeter Zijlstra 
4462a5bd6236SJosh Poimboeuf 		if (!sec->rsec)
446308f87a93SPeter Zijlstra 			continue;
446408f87a93SPeter Zijlstra 
44653c6f9f77SJosh Poimboeuf 		/*
44663c6f9f77SJosh Poimboeuf 		 * These sections can reference text addresses, but not with
44673c6f9f77SJosh Poimboeuf 		 * the intent to indirect branch to them.
44683c6f9f77SJosh Poimboeuf 		 */
4469e27e5beaSJosh Poimboeuf 		if ((!strncmp(sec->name, ".discard", 8) &&
4470e27e5beaSJosh Poimboeuf 		     strcmp(sec->name, ".discard.ibt_endbr_noseal"))	||
44713c6f9f77SJosh Poimboeuf 		    !strncmp(sec->name, ".debug", 6)			||
44723c6f9f77SJosh Poimboeuf 		    !strcmp(sec->name, ".altinstructions")		||
44733c6f9f77SJosh Poimboeuf 		    !strcmp(sec->name, ".ibt_endbr_seal")		||
44743c6f9f77SJosh Poimboeuf 		    !strcmp(sec->name, ".orc_unwind_ip")		||
44753c6f9f77SJosh Poimboeuf 		    !strcmp(sec->name, ".parainstructions")		||
44763c6f9f77SJosh Poimboeuf 		    !strcmp(sec->name, ".retpoline_sites")		||
44773c6f9f77SJosh Poimboeuf 		    !strcmp(sec->name, ".smp_locks")			||
44783c6f9f77SJosh Poimboeuf 		    !strcmp(sec->name, ".static_call_sites")		||
44793c6f9f77SJosh Poimboeuf 		    !strcmp(sec->name, "_error_injection_whitelist")	||
44803c6f9f77SJosh Poimboeuf 		    !strcmp(sec->name, "_kprobe_blacklist")		||
44813c6f9f77SJosh Poimboeuf 		    !strcmp(sec->name, "__bug_table")			||
44823c6f9f77SJosh Poimboeuf 		    !strcmp(sec->name, "__ex_table")			||
44833c6f9f77SJosh Poimboeuf 		    !strcmp(sec->name, "__jump_table")			||
44843c68a92dSSami Tolvanen 		    !strcmp(sec->name, "__mcount_loc")			||
44850326074fSLinus Torvalds 		    !strcmp(sec->name, ".kcfi_traps")			||
4486315ad878SRong Xu 		    !strcmp(sec->name, ".llvm.call-graph-profile")	||
4487d5dc9583SRong Xu 		    !strcmp(sec->name, ".llvm_bb_addr_map")		||
4488d5173f75SPeter Zijlstra 		    !strcmp(sec->name, "__tracepoints")			||
44899440155cSPeter Zijlstra (Intel) 		    strstr(sec->name, "__patchable_function_entries"))
449008f87a93SPeter Zijlstra 			continue;
449108f87a93SPeter Zijlstra 
4492caa4a6b7SJosh Poimboeuf 		for_each_reloc(sec->rsec, reloc)
44933c6f9f77SJosh Poimboeuf 			warnings += validate_ibt_data_reloc(file, reloc);
449408f87a93SPeter Zijlstra 	}
449508f87a93SPeter Zijlstra 
44963c6f9f77SJosh Poimboeuf 	return warnings;
449708f87a93SPeter Zijlstra }
449808f87a93SPeter Zijlstra 
validate_sls(struct objtool_file * file)4499c2bdd61cSJosh Poimboeuf static int validate_sls(struct objtool_file *file)
4500c2bdd61cSJosh Poimboeuf {
4501c2bdd61cSJosh Poimboeuf 	struct instruction *insn, *next_insn;
4502c2bdd61cSJosh Poimboeuf 	int warnings = 0;
4503c2bdd61cSJosh Poimboeuf 
4504c2bdd61cSJosh Poimboeuf 	for_each_insn(file, insn) {
4505c2bdd61cSJosh Poimboeuf 		next_insn = next_insn_same_sec(file, insn);
4506c2bdd61cSJosh Poimboeuf 
4507c2bdd61cSJosh Poimboeuf 		if (insn->retpoline_safe)
4508c2bdd61cSJosh Poimboeuf 			continue;
4509c2bdd61cSJosh Poimboeuf 
4510c2bdd61cSJosh Poimboeuf 		switch (insn->type) {
4511c2bdd61cSJosh Poimboeuf 		case INSN_RETURN:
4512c2bdd61cSJosh Poimboeuf 			if (!next_insn || next_insn->type != INSN_TRAP) {
4513246b2c85SJosh Poimboeuf 				WARN_INSN(insn, "missing int3 after ret");
4514c2bdd61cSJosh Poimboeuf 				warnings++;
4515c2bdd61cSJosh Poimboeuf 			}
4516c2bdd61cSJosh Poimboeuf 
4517c2bdd61cSJosh Poimboeuf 			break;
4518c2bdd61cSJosh Poimboeuf 		case INSN_JUMP_DYNAMIC:
4519c2bdd61cSJosh Poimboeuf 			if (!next_insn || next_insn->type != INSN_TRAP) {
4520246b2c85SJosh Poimboeuf 				WARN_INSN(insn, "missing int3 after indirect jump");
4521c2bdd61cSJosh Poimboeuf 				warnings++;
4522c2bdd61cSJosh Poimboeuf 			}
4523c2bdd61cSJosh Poimboeuf 			break;
4524c2bdd61cSJosh Poimboeuf 		default:
4525c2bdd61cSJosh Poimboeuf 			break;
4526c2bdd61cSJosh Poimboeuf 		}
4527c2bdd61cSJosh Poimboeuf 	}
4528c2bdd61cSJosh Poimboeuf 
4529c2bdd61cSJosh Poimboeuf 	return warnings;
4530c2bdd61cSJosh Poimboeuf }
4531c2bdd61cSJosh Poimboeuf 
validate_reachable_instructions(struct objtool_file * file)4532baa41469SJosh Poimboeuf static int validate_reachable_instructions(struct objtool_file *file)
4533baa41469SJosh Poimboeuf {
4534fedb724cSJosh Poimboeuf 	struct instruction *insn, *prev_insn;
4535fedb724cSJosh Poimboeuf 	struct symbol *call_dest;
45365e3992feSJosh Poimboeuf 	int warnings = 0;
4537dcc914f4SJosh Poimboeuf 
4538baa41469SJosh Poimboeuf 	if (file->ignore_unreachables)
4539baa41469SJosh Poimboeuf 		return 0;
4540dcc914f4SJosh Poimboeuf 
4541baa41469SJosh Poimboeuf 	for_each_insn(file, insn) {
454214db1f0aSIlie Halip 		if (insn->visited || ignore_unreachable_insn(file, insn))
4543dcc914f4SJosh Poimboeuf 			continue;
4544dcc914f4SJosh Poimboeuf 
4545fedb724cSJosh Poimboeuf 		prev_insn = prev_insn_same_sec(file, insn);
4546fedb724cSJosh Poimboeuf 		if (prev_insn && prev_insn->dead_end) {
4547fedb724cSJosh Poimboeuf 			call_dest = insn_call_dest(prev_insn);
45488085fcd7SJosh Poimboeuf 			if (call_dest) {
4549acae6b5bSJosh Poimboeuf 				WARN_INSN(insn, "%s() missing __noreturn in .c/.h or NORETURN() in noreturns.h",
4550fedb724cSJosh Poimboeuf 					  call_dest->name);
4551fedb724cSJosh Poimboeuf 				warnings++;
4552fedb724cSJosh Poimboeuf 				continue;
4553fedb724cSJosh Poimboeuf 			}
4554fedb724cSJosh Poimboeuf 		}
4555fedb724cSJosh Poimboeuf 
4556246b2c85SJosh Poimboeuf 		WARN_INSN(insn, "unreachable instruction");
45575e3992feSJosh Poimboeuf 		warnings++;
4558dcc914f4SJosh Poimboeuf 	}
4559dcc914f4SJosh Poimboeuf 
45605e3992feSJosh Poimboeuf 	return warnings;
4561dcc914f4SJosh Poimboeuf }
4562dcc914f4SJosh Poimboeuf 
4563ca653464SJosh Poimboeuf /* 'funcs' is a space-separated list of function names */
disas_funcs(const char * funcs)4564c5995abeSJosh Poimboeuf static void disas_funcs(const char *funcs)
4565ca653464SJosh Poimboeuf {
4566ca653464SJosh Poimboeuf 	const char *objdump_str, *cross_compile;
4567ca653464SJosh Poimboeuf 	int size, ret;
4568ca653464SJosh Poimboeuf 	char *cmd;
4569ca653464SJosh Poimboeuf 
4570ca653464SJosh Poimboeuf 	cross_compile = getenv("CROSS_COMPILE");
4571e77956e4SDavid Laight 	if (!cross_compile)
4572e77956e4SDavid Laight 		cross_compile = "";
4573ca653464SJosh Poimboeuf 
4574ca653464SJosh Poimboeuf 	objdump_str = "%sobjdump -wdr %s | gawk -M -v _funcs='%s' '"
4575ca653464SJosh Poimboeuf 			"BEGIN { split(_funcs, funcs); }"
4576ca653464SJosh Poimboeuf 			"/^$/ { func_match = 0; }"
4577ca653464SJosh Poimboeuf 			"/<.*>:/ { "
4578ca653464SJosh Poimboeuf 				"f = gensub(/.*<(.*)>:/, \"\\\\1\", 1);"
4579ca653464SJosh Poimboeuf 				"for (i in funcs) {"
4580ca653464SJosh Poimboeuf 					"if (funcs[i] == f) {"
4581ca653464SJosh Poimboeuf 						"func_match = 1;"
4582ca653464SJosh Poimboeuf 						"base = strtonum(\"0x\" $1);"
4583ca653464SJosh Poimboeuf 						"break;"
4584ca653464SJosh Poimboeuf 					"}"
4585ca653464SJosh Poimboeuf 				"}"
4586ca653464SJosh Poimboeuf 			"}"
4587ca653464SJosh Poimboeuf 			"{"
4588ca653464SJosh Poimboeuf 				"if (func_match) {"
4589ca653464SJosh Poimboeuf 					"addr = strtonum(\"0x\" $1);"
4590ca653464SJosh Poimboeuf 					"printf(\"%%04x \", addr - base);"
4591ca653464SJosh Poimboeuf 					"print;"
4592ca653464SJosh Poimboeuf 				"}"
4593ca653464SJosh Poimboeuf 			"}' 1>&2";
4594ca653464SJosh Poimboeuf 
4595ca653464SJosh Poimboeuf 	/* fake snprintf() to calculate the size */
4596ca653464SJosh Poimboeuf 	size = snprintf(NULL, 0, objdump_str, cross_compile, objname, funcs) + 1;
4597ca653464SJosh Poimboeuf 	if (size <= 0) {
4598ca653464SJosh Poimboeuf 		WARN("objdump string size calculation failed");
4599c5995abeSJosh Poimboeuf 		return;
4600ca653464SJosh Poimboeuf 	}
4601ca653464SJosh Poimboeuf 
4602ca653464SJosh Poimboeuf 	cmd = malloc(size);
4603ca653464SJosh Poimboeuf 
4604ca653464SJosh Poimboeuf 	/* real snprintf() */
4605ca653464SJosh Poimboeuf 	snprintf(cmd, size, objdump_str, cross_compile, objname, funcs);
4606ca653464SJosh Poimboeuf 	ret = system(cmd);
4607ca653464SJosh Poimboeuf 	if (ret) {
4608ca653464SJosh Poimboeuf 		WARN("disassembly failed: %d", ret);
4609c5995abeSJosh Poimboeuf 		return;
4610c5995abeSJosh Poimboeuf 	}
4611ca653464SJosh Poimboeuf }
4612ca653464SJosh Poimboeuf 
disas_warned_funcs(struct objtool_file * file)4613c5995abeSJosh Poimboeuf static void disas_warned_funcs(struct objtool_file *file)
4614ca653464SJosh Poimboeuf {
4615ca653464SJosh Poimboeuf 	struct symbol *sym;
4616ca653464SJosh Poimboeuf 	char *funcs = NULL, *tmp;
4617ca653464SJosh Poimboeuf 
4618ca653464SJosh Poimboeuf 	for_each_sym(file, sym) {
4619c5610071SJosh Poimboeuf 		if (sym->warned) {
4620ca653464SJosh Poimboeuf 			if (!funcs) {
4621ca653464SJosh Poimboeuf 				funcs = malloc(strlen(sym->name) + 1);
4622c5995abeSJosh Poimboeuf 				if (!funcs) {
46233e7be635SJosh Poimboeuf 					ERROR_GLIBC("malloc");
4624c5995abeSJosh Poimboeuf 					return;
4625c5995abeSJosh Poimboeuf 				}
4626ca653464SJosh Poimboeuf 				strcpy(funcs, sym->name);
4627ca653464SJosh Poimboeuf 			} else {
4628ca653464SJosh Poimboeuf 				tmp = malloc(strlen(funcs) + strlen(sym->name) + 2);
4629c5995abeSJosh Poimboeuf 				if (!tmp) {
46303e7be635SJosh Poimboeuf 					ERROR_GLIBC("malloc");
4631c5995abeSJosh Poimboeuf 					return;
4632c5995abeSJosh Poimboeuf 				}
4633ca653464SJosh Poimboeuf 				sprintf(tmp, "%s %s", funcs, sym->name);
4634ca653464SJosh Poimboeuf 				free(funcs);
4635ca653464SJosh Poimboeuf 				funcs = tmp;
4636ca653464SJosh Poimboeuf 			}
4637ca653464SJosh Poimboeuf 		}
4638ca653464SJosh Poimboeuf 	}
4639ca653464SJosh Poimboeuf 
4640ca653464SJosh Poimboeuf 	if (funcs)
4641ca653464SJosh Poimboeuf 		disas_funcs(funcs);
4642ca653464SJosh Poimboeuf }
4643ca653464SJosh Poimboeuf 
4644d93b5935SJosh Poimboeuf struct insn_chunk {
4645d93b5935SJosh Poimboeuf 	void *addr;
4646d93b5935SJosh Poimboeuf 	struct insn_chunk *next;
4647d93b5935SJosh Poimboeuf };
4648d93b5935SJosh Poimboeuf 
4649d93b5935SJosh Poimboeuf /*
4650d93b5935SJosh Poimboeuf  * Reduce peak RSS usage by freeing insns memory before writing the ELF file,
4651d93b5935SJosh Poimboeuf  * which can trigger more allocations for .debug_* sections whose data hasn't
4652d93b5935SJosh Poimboeuf  * been read yet.
4653d93b5935SJosh Poimboeuf  */
free_insns(struct objtool_file * file)4654d93b5935SJosh Poimboeuf static void free_insns(struct objtool_file *file)
4655d93b5935SJosh Poimboeuf {
4656d93b5935SJosh Poimboeuf 	struct instruction *insn;
4657d93b5935SJosh Poimboeuf 	struct insn_chunk *chunks = NULL, *chunk;
4658d93b5935SJosh Poimboeuf 
4659d93b5935SJosh Poimboeuf 	for_each_insn(file, insn) {
4660d93b5935SJosh Poimboeuf 		if (!insn->idx) {
4661d93b5935SJosh Poimboeuf 			chunk = malloc(sizeof(*chunk));
4662d93b5935SJosh Poimboeuf 			chunk->addr = insn;
4663d93b5935SJosh Poimboeuf 			chunk->next = chunks;
4664d93b5935SJosh Poimboeuf 			chunks = chunk;
4665d93b5935SJosh Poimboeuf 		}
4666d93b5935SJosh Poimboeuf 	}
4667d93b5935SJosh Poimboeuf 
4668d93b5935SJosh Poimboeuf 	for (chunk = chunks; chunk; chunk = chunk->next)
4669d93b5935SJosh Poimboeuf 		free(chunk->addr);
4670d93b5935SJosh Poimboeuf }
4671d93b5935SJosh Poimboeuf 
check(struct objtool_file * file)4672d44becb9SJulien Thierry int check(struct objtool_file *file)
4673dcc914f4SJosh Poimboeuf {
4674c5995abeSJosh Poimboeuf 	int ret = 0, warnings = 0;
4675dcc914f4SJosh Poimboeuf 
4676baa41469SJosh Poimboeuf 	arch_initial_func_cfi_state(&initial_func_cfi);
46778b946cc3SPeter Zijlstra 	init_cfi_state(&init_cfi);
46788b946cc3SPeter Zijlstra 	init_cfi_state(&func_cfi);
46798b946cc3SPeter Zijlstra 	set_func_state(&func_cfi);
46801e4b6191SJosh Poimboeuf 	init_cfi_state(&force_undefined_cfi);
46811e4b6191SJosh Poimboeuf 	force_undefined_cfi.force_undefined = true;
46828b946cc3SPeter Zijlstra 
4683b745962cSJosh Poimboeuf 	if (!cfi_hash_alloc(1UL << (file->elf->symbol_bits - 3))) {
4684b745962cSJosh Poimboeuf 		ret = -1;
46858b946cc3SPeter Zijlstra 		goto out;
4686b745962cSJosh Poimboeuf 	}
46878b946cc3SPeter Zijlstra 
46888b946cc3SPeter Zijlstra 	cfi_hash_add(&init_cfi);
46898b946cc3SPeter Zijlstra 	cfi_hash_add(&func_cfi);
4690baa41469SJosh Poimboeuf 
46916545eb03SJulien Thierry 	ret = decode_sections(file);
4692c5995abeSJosh Poimboeuf 	if (ret)
4693dcc914f4SJosh Poimboeuf 		goto out;
46948b946cc3SPeter Zijlstra 
46951c34496eSPeter Zijlstra 	if (!nr_insns)
4696baa41469SJosh Poimboeuf 		goto out;
4697baa41469SJosh Poimboeuf 
4698c5995abeSJosh Poimboeuf 	if (opts.retpoline)
4699c5995abeSJosh Poimboeuf 		warnings += validate_retpoline(file);
4700b5bc2231SPeter Zijlstra 
4701c2bdd61cSJosh Poimboeuf 	if (opts.stackval || opts.orc || opts.uaccess) {
4702c5995abeSJosh Poimboeuf 		int w = 0;
4703dcc914f4SJosh Poimboeuf 
4704c5995abeSJosh Poimboeuf 		w += validate_functions(file);
4705c5995abeSJosh Poimboeuf 		w += validate_unwind_hints(file, NULL);
4706c5995abeSJosh Poimboeuf 		if (!w)
4707c5995abeSJosh Poimboeuf 			w += validate_reachable_instructions(file);
470839358a03SJosh Poimboeuf 
4709c5995abeSJosh Poimboeuf 		warnings += w;
4710753da417SJosh Poimboeuf 
4711753da417SJosh Poimboeuf 	} else if (opts.noinstr) {
4712c5995abeSJosh Poimboeuf 		warnings += validate_noinstr_sections(file);
47137dce6204SJosh Poimboeuf 	}
471408f87a93SPeter Zijlstra 
4715a09a6e23SPeter Zijlstra 	if (opts.unret) {
4716a09a6e23SPeter Zijlstra 		/*
4717a09a6e23SPeter Zijlstra 		 * Must be after validate_branch() and friends, it plays
4718a09a6e23SPeter Zijlstra 		 * further games with insn->visited.
4719a09a6e23SPeter Zijlstra 		 */
4720c5995abeSJosh Poimboeuf 		warnings += validate_unrets(file);
4721a09a6e23SPeter Zijlstra 	}
4722a09a6e23SPeter Zijlstra 
4723c5995abeSJosh Poimboeuf 	if (opts.ibt)
4724c5995abeSJosh Poimboeuf 		warnings += validate_ibt(file);
4725dcc914f4SJosh Poimboeuf 
4726c5995abeSJosh Poimboeuf 	if (opts.sls)
4727c5995abeSJosh Poimboeuf 		warnings += validate_sls(file);
4728c2bdd61cSJosh Poimboeuf 
472926e17689SJosh Poimboeuf 	if (opts.static_call) {
47306545eb03SJulien Thierry 		ret = create_static_call_sections(file);
4731c5995abeSJosh Poimboeuf 		if (ret)
47321e7e4788SJosh Poimboeuf 			goto out;
473326e17689SJosh Poimboeuf 	}
47341e7e4788SJosh Poimboeuf 
47352daf7fabSJosh Poimboeuf 	if (opts.retpoline) {
4736134ab5bdSPeter Zijlstra 		ret = create_retpoline_sites_sections(file);
4737c5995abeSJosh Poimboeuf 		if (ret)
4738134ab5bdSPeter Zijlstra 			goto out;
4739134ab5bdSPeter Zijlstra 	}
4740134ab5bdSPeter Zijlstra 
47419a479f76SPeter Zijlstra 	if (opts.cfi) {
47429a479f76SPeter Zijlstra 		ret = create_cfi_sections(file);
4743c5995abeSJosh Poimboeuf 		if (ret)
47449a479f76SPeter Zijlstra 			goto out;
47459a479f76SPeter Zijlstra 	}
47469a479f76SPeter Zijlstra 
4747f43b9876SPeter Zijlstra 	if (opts.rethunk) {
4748d9e9d230SPeter Zijlstra 		ret = create_return_sites_sections(file);
4749c5995abeSJosh Poimboeuf 		if (ret)
4750d9e9d230SPeter Zijlstra 			goto out;
475100abd384SPeter Zijlstra 
47520c0a6d89SPeter Zijlstra 		if (opts.hack_skylake) {
475300abd384SPeter Zijlstra 			ret = create_direct_call_sections(file);
4754c5995abeSJosh Poimboeuf 			if (ret)
475500abd384SPeter Zijlstra 				goto out;
4756dcc914f4SJosh Poimboeuf 		}
4757dcc914f4SJosh Poimboeuf 	}
4758dcc914f4SJosh Poimboeuf 
47592daf7fabSJosh Poimboeuf 	if (opts.mcount) {
4760dcc914f4SJosh Poimboeuf 		ret = create_mcount_loc_sections(file);
4761c5995abeSJosh Poimboeuf 		if (ret)
4762dcc914f4SJosh Poimboeuf 			goto out;
4763dcc914f4SJosh Poimboeuf 	}
4764dcc914f4SJosh Poimboeuf 
4765bd456a1bSJosh Poimboeuf 	if (opts.prefix) {
4766bd456a1bSJosh Poimboeuf 		ret = add_prefix_symbols(file);
4767c5995abeSJosh Poimboeuf 		if (ret)
4768b745962cSJosh Poimboeuf 			goto out;
4769bd456a1bSJosh Poimboeuf 	}
4770bd456a1bSJosh Poimboeuf 
47712daf7fabSJosh Poimboeuf 	if (opts.ibt) {
477289bc853eSPeter Zijlstra 		ret = create_ibt_endbr_seal_sections(file);
4773c5995abeSJosh Poimboeuf 		if (ret)
477489bc853eSPeter Zijlstra 			goto out;
477589bc853eSPeter Zijlstra 	}
477689bc853eSPeter Zijlstra 
47771c34496eSPeter Zijlstra 	if (opts.orc && nr_insns) {
4778b51277ebSJosh Poimboeuf 		ret = orc_create(file);
4779c5995abeSJosh Poimboeuf 		if (ret)
4780b51277ebSJosh Poimboeuf 			goto out;
4781b51277ebSJosh Poimboeuf 	}
4782b51277ebSJosh Poimboeuf 
4783d93b5935SJosh Poimboeuf 	free_insns(file);
4784d93b5935SJosh Poimboeuf 
47852daf7fabSJosh Poimboeuf 	if (opts.stats) {
4786dcc914f4SJosh Poimboeuf 		printf("nr_insns_visited: %ld\n", nr_insns_visited);
4787dcc914f4SJosh Poimboeuf 		printf("nr_cfi: %ld\n", nr_cfi);
4788dcc914f4SJosh Poimboeuf 		printf("nr_cfi_reused: %ld\n", nr_cfi_reused);
4789dcc914f4SJosh Poimboeuf 		printf("nr_cfi_cache: %ld\n", nr_cfi_cache);
4790dcc914f4SJosh Poimboeuf 	}
4791dcc914f4SJosh Poimboeuf 
4792dcc914f4SJosh Poimboeuf out:
4793d39f82a0SJosh Poimboeuf 	if (!ret && !warnings)
4794d39f82a0SJosh Poimboeuf 		return 0;
4795d39f82a0SJosh Poimboeuf 
47960b101771SJosh Poimboeuf 	if (opts.werror && warnings)
47970b101771SJosh Poimboeuf 		ret = 1;
47980b101771SJosh Poimboeuf 
4799d39f82a0SJosh Poimboeuf 	if (opts.verbose) {
4800d39f82a0SJosh Poimboeuf 		if (opts.werror && warnings)
4801d39f82a0SJosh Poimboeuf 			WARN("%d warning(s) upgraded to errors", warnings);
4802d39f82a0SJosh Poimboeuf 		print_args();
4803d39f82a0SJosh Poimboeuf 		disas_warned_funcs(file);
4804d39f82a0SJosh Poimboeuf 	}
4805d39f82a0SJosh Poimboeuf 
48060b101771SJosh Poimboeuf 	return ret;
4807dcc914f4SJosh Poimboeuf }
4808