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(®s[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