blob: ea80b29b99134d242a7c21a901fbe43ebb233717 [file] [log] [blame]
David Brazdil0f672f62019-12-10 10:32:29 +00001// SPDX-License-Identifier: GPL-2.0-or-later
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002/*
3 * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00004 */
5
6#include <string.h>
7#include <stdlib.h>
Olivier Deprez92d4c212022-12-06 15:05:30 +01008#include <inttypes.h>
9#include <sys/mman.h>
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000010
11#include "builtin.h"
Olivier Deprez157378f2022-04-04 15:47:50 +020012#include "cfi.h"
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000013#include "arch.h"
Olivier Deprez157378f2022-04-04 15:47:50 +020014#include "check.h"
15#include "special.h"
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000016#include "warn.h"
Olivier Deprez157378f2022-04-04 15:47:50 +020017#include "arch_elf.h"
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000018
Olivier Deprez157378f2022-04-04 15:47:50 +020019#include <linux/objtool.h>
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000020#include <linux/hashtable.h>
21#include <linux/kernel.h>
Olivier Deprez157378f2022-04-04 15:47:50 +020022#include <linux/static_call_types.h>
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000023
24struct alternative {
25 struct list_head list;
26 struct instruction *insn;
David Brazdil0f672f62019-12-10 10:32:29 +000027 bool skip_orig;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000028};
29
Olivier Deprez92d4c212022-12-06 15:05:30 +010030static unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache;
31
32static struct cfi_init_state initial_func_cfi;
33static struct cfi_state init_cfi;
34static struct cfi_state func_cfi;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000035
36struct instruction *find_insn(struct objtool_file *file,
37 struct section *sec, unsigned long offset)
38{
39 struct instruction *insn;
40
Olivier Deprez157378f2022-04-04 15:47:50 +020041 hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, offset)) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000042 if (insn->sec == sec && insn->offset == offset)
43 return insn;
Olivier Deprez157378f2022-04-04 15:47:50 +020044 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000045
46 return NULL;
47}
48
49static struct instruction *next_insn_same_sec(struct objtool_file *file,
50 struct instruction *insn)
51{
52 struct instruction *next = list_next_entry(insn, list);
53
54 if (!next || &next->list == &file->insn_list || next->sec != insn->sec)
55 return NULL;
56
57 return next;
58}
59
60static struct instruction *next_insn_same_func(struct objtool_file *file,
61 struct instruction *insn)
62{
63 struct instruction *next = list_next_entry(insn, list);
64 struct symbol *func = insn->func;
65
66 if (!func)
67 return NULL;
68
69 if (&next->list != &file->insn_list && next->func == func)
70 return next;
71
72 /* Check if we're already in the subfunction: */
73 if (func == func->cfunc)
74 return NULL;
75
76 /* Move to the subfunction: */
77 return find_insn(file, func->cfunc->sec, func->cfunc->offset);
78}
79
Olivier Deprez157378f2022-04-04 15:47:50 +020080static struct instruction *prev_insn_same_sym(struct objtool_file *file,
81 struct instruction *insn)
82{
83 struct instruction *prev = list_prev_entry(insn, list);
84
85 if (&prev->list != &file->insn_list && prev->func == insn->func)
86 return prev;
87
88 return NULL;
89}
90
91#define func_for_each_insn(file, func, insn) \
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000092 for (insn = find_insn(file, func->sec, func->offset); \
93 insn; \
94 insn = next_insn_same_func(file, insn))
95
Olivier Deprez157378f2022-04-04 15:47:50 +020096#define sym_for_each_insn(file, sym, insn) \
97 for (insn = find_insn(file, sym->sec, sym->offset); \
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000098 insn && &insn->list != &file->insn_list && \
Olivier Deprez157378f2022-04-04 15:47:50 +020099 insn->sec == sym->sec && \
100 insn->offset < sym->offset + sym->len; \
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000101 insn = list_next_entry(insn, list))
102
Olivier Deprez157378f2022-04-04 15:47:50 +0200103#define sym_for_each_insn_continue_reverse(file, sym, insn) \
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000104 for (insn = list_prev_entry(insn, list); \
105 &insn->list != &file->insn_list && \
Olivier Deprez157378f2022-04-04 15:47:50 +0200106 insn->sec == sym->sec && insn->offset >= sym->offset; \
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000107 insn = list_prev_entry(insn, list))
108
109#define sec_for_each_insn_from(file, insn) \
110 for (; insn; insn = next_insn_same_sec(file, insn))
111
112#define sec_for_each_insn_continue(file, insn) \
113 for (insn = next_insn_same_sec(file, insn); insn; \
114 insn = next_insn_same_sec(file, insn))
115
Olivier Deprez92d4c212022-12-06 15:05:30 +0100116static bool is_jump_table_jump(struct instruction *insn)
117{
118 struct alt_group *alt_group = insn->alt_group;
119
120 if (insn->jump_table)
121 return true;
122
123 /* Retpoline alternative for a jump table? */
124 return alt_group && alt_group->orig_group &&
125 alt_group->orig_group->first_insn->jump_table;
126}
127
David Brazdil0f672f62019-12-10 10:32:29 +0000128static bool is_sibling_call(struct instruction *insn)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000129{
Olivier Deprez92d4c212022-12-06 15:05:30 +0100130 /*
131 * Assume only ELF functions can make sibling calls. This ensures
132 * sibling call detection consistency between vmlinux.o and individual
133 * objects.
134 */
135 if (!insn->func)
David Brazdil0f672f62019-12-10 10:32:29 +0000136 return false;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000137
Olivier Deprez92d4c212022-12-06 15:05:30 +0100138 /* An indirect jump is either a sibling call or a jump to a table. */
139 if (insn->type == INSN_JUMP_DYNAMIC)
140 return !is_jump_table_jump(insn);
141
David Brazdil0f672f62019-12-10 10:32:29 +0000142 /* add_jump_destinations() sets insn->call_dest for sibling calls. */
Olivier Deprez92d4c212022-12-06 15:05:30 +0100143 return (is_static_jump(insn) && insn->call_dest);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000144}
145
146/*
147 * This checks to see if the given function is a "noreturn" function.
148 *
149 * For global functions which are outside the scope of this object file, we
150 * have to keep a manual list of them.
151 *
152 * For local functions, we have to detect them manually by simply looking for
153 * the lack of a return instruction.
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000154 */
David Brazdil0f672f62019-12-10 10:32:29 +0000155static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
156 int recursion)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000157{
158 int i;
159 struct instruction *insn;
160 bool empty = true;
161
162 /*
163 * Unfortunately these have to be hard coded because the noreturn
164 * attribute isn't provided in ELF data.
165 */
166 static const char * const global_noreturns[] = {
167 "__stack_chk_fail",
168 "panic",
169 "do_exit",
170 "do_task_dead",
171 "__module_put_and_exit",
172 "complete_and_exit",
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000173 "__reiserfs_panic",
174 "lbug_with_loc",
175 "fortify_panic",
176 "usercopy_abort",
177 "machine_real_restart",
David Brazdil0f672f62019-12-10 10:32:29 +0000178 "rewind_stack_do_exit",
Olivier Deprez157378f2022-04-04 15:47:50 +0200179 "kunit_try_catch_throw",
180 "xen_start_kernel",
181 "cpu_bringup_and_idle",
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000182 };
183
David Brazdil0f672f62019-12-10 10:32:29 +0000184 if (!func)
185 return false;
186
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000187 if (func->bind == STB_WEAK)
David Brazdil0f672f62019-12-10 10:32:29 +0000188 return false;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000189
190 if (func->bind == STB_GLOBAL)
191 for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
192 if (!strcmp(func->name, global_noreturns[i]))
David Brazdil0f672f62019-12-10 10:32:29 +0000193 return true;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000194
195 if (!func->len)
David Brazdil0f672f62019-12-10 10:32:29 +0000196 return false;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000197
198 insn = find_insn(file, func->sec, func->offset);
199 if (!insn->func)
David Brazdil0f672f62019-12-10 10:32:29 +0000200 return false;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000201
Olivier Deprez157378f2022-04-04 15:47:50 +0200202 func_for_each_insn(file, func, insn) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000203 empty = false;
204
205 if (insn->type == INSN_RETURN)
David Brazdil0f672f62019-12-10 10:32:29 +0000206 return false;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000207 }
208
209 if (empty)
David Brazdil0f672f62019-12-10 10:32:29 +0000210 return false;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000211
212 /*
213 * A function can have a sibling call instead of a return. In that
214 * case, the function's dead-end status depends on whether the target
215 * of the sibling call returns.
216 */
Olivier Deprez157378f2022-04-04 15:47:50 +0200217 func_for_each_insn(file, func, insn) {
David Brazdil0f672f62019-12-10 10:32:29 +0000218 if (is_sibling_call(insn)) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000219 struct instruction *dest = insn->jump_dest;
220
221 if (!dest)
222 /* sibling call to another file */
David Brazdil0f672f62019-12-10 10:32:29 +0000223 return false;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000224
David Brazdil0f672f62019-12-10 10:32:29 +0000225 /* local sibling call */
226 if (recursion == 5) {
227 /*
228 * Infinite recursion: two functions have
229 * sibling calls to each other. This is a very
230 * rare case. It means they aren't dead ends.
231 */
232 return false;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000233 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000234
David Brazdil0f672f62019-12-10 10:32:29 +0000235 return __dead_end_function(file, dest->func, recursion+1);
236 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000237 }
238
David Brazdil0f672f62019-12-10 10:32:29 +0000239 return true;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000240}
241
David Brazdil0f672f62019-12-10 10:32:29 +0000242static bool dead_end_function(struct objtool_file *file, struct symbol *func)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000243{
244 return __dead_end_function(file, func, 0);
245}
246
Olivier Deprez157378f2022-04-04 15:47:50 +0200247static void init_cfi_state(struct cfi_state *cfi)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000248{
249 int i;
250
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000251 for (i = 0; i < CFI_NUM_REGS; i++) {
Olivier Deprez157378f2022-04-04 15:47:50 +0200252 cfi->regs[i].base = CFI_UNDEFINED;
253 cfi->vals[i].base = CFI_UNDEFINED;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000254 }
Olivier Deprez157378f2022-04-04 15:47:50 +0200255 cfi->cfa.base = CFI_UNDEFINED;
256 cfi->drap_reg = CFI_UNDEFINED;
257 cfi->drap_offset = -1;
258}
259
260static void init_insn_state(struct insn_state *state, struct section *sec)
261{
262 memset(state, 0, sizeof(*state));
263 init_cfi_state(&state->cfi);
264
265 /*
266 * We need the full vmlinux for noinstr validation, otherwise we can
267 * not correctly determine insn->call_dest->sec (external symbols do
268 * not have a section).
269 */
270 if (vmlinux && sec)
271 state->noinstr = sec->noinstr;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000272}
273
Olivier Deprez92d4c212022-12-06 15:05:30 +0100274static struct cfi_state *cfi_alloc(void)
275{
276 struct cfi_state *cfi = calloc(sizeof(struct cfi_state), 1);
277 if (!cfi) {
278 WARN("calloc failed");
279 exit(1);
280 }
281 nr_cfi++;
282 return cfi;
283}
284
285static int cfi_bits;
286static struct hlist_head *cfi_hash;
287
288static inline bool cficmp(struct cfi_state *cfi1, struct cfi_state *cfi2)
289{
290 return memcmp((void *)cfi1 + sizeof(cfi1->hash),
291 (void *)cfi2 + sizeof(cfi2->hash),
292 sizeof(struct cfi_state) - sizeof(struct hlist_node));
293}
294
295static inline u32 cfi_key(struct cfi_state *cfi)
296{
297 return jhash((void *)cfi + sizeof(cfi->hash),
298 sizeof(*cfi) - sizeof(cfi->hash), 0);
299}
300
301static struct cfi_state *cfi_hash_find_or_add(struct cfi_state *cfi)
302{
303 struct hlist_head *head = &cfi_hash[hash_min(cfi_key(cfi), cfi_bits)];
304 struct cfi_state *obj;
305
306 hlist_for_each_entry(obj, head, hash) {
307 if (!cficmp(cfi, obj)) {
308 nr_cfi_cache++;
309 return obj;
310 }
311 }
312
313 obj = cfi_alloc();
314 *obj = *cfi;
315 hlist_add_head(&obj->hash, head);
316
317 return obj;
318}
319
320static void cfi_hash_add(struct cfi_state *cfi)
321{
322 struct hlist_head *head = &cfi_hash[hash_min(cfi_key(cfi), cfi_bits)];
323
324 hlist_add_head(&cfi->hash, head);
325}
326
327static void *cfi_hash_alloc(void)
328{
329 cfi_bits = vmlinux ? ELF_HASH_BITS - 3 : 13;
330 cfi_hash = mmap(NULL, sizeof(struct hlist_head) << cfi_bits,
331 PROT_READ|PROT_WRITE,
332 MAP_PRIVATE|MAP_ANON, -1, 0);
333 if (cfi_hash == (void *)-1L) {
334 WARN("mmap fail cfi_hash");
335 cfi_hash = NULL;
336 } else if (stats) {
337 printf("cfi_bits: %d\n", cfi_bits);
338 }
339
340 return cfi_hash;
341}
342
343static unsigned long nr_insns;
344static unsigned long nr_insns_visited;
345
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000346/*
347 * Call the arch-specific instruction decoder for all the instructions and add
348 * them to the global instruction list.
349 */
350static int decode_instructions(struct objtool_file *file)
351{
352 struct section *sec;
353 struct symbol *func;
354 unsigned long offset;
355 struct instruction *insn;
356 int ret;
357
358 for_each_sec(file, sec) {
359
360 if (!(sec->sh.sh_flags & SHF_EXECINSTR))
361 continue;
362
363 if (strcmp(sec->name, ".altinstr_replacement") &&
364 strcmp(sec->name, ".altinstr_aux") &&
365 strncmp(sec->name, ".discard.", 9))
366 sec->text = true;
367
Olivier Deprez157378f2022-04-04 15:47:50 +0200368 if (!strcmp(sec->name, ".noinstr.text") ||
Olivier Deprez92d4c212022-12-06 15:05:30 +0100369 !strcmp(sec->name, ".entry.text") ||
370 !strncmp(sec->name, ".text.__x86.", 12))
Olivier Deprez157378f2022-04-04 15:47:50 +0200371 sec->noinstr = true;
372
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000373 for (offset = 0; offset < sec->len; offset += insn->len) {
374 insn = malloc(sizeof(*insn));
375 if (!insn) {
376 WARN("malloc failed");
377 return -1;
378 }
379 memset(insn, 0, sizeof(*insn));
380 INIT_LIST_HEAD(&insn->alts);
Olivier Deprez157378f2022-04-04 15:47:50 +0200381 INIT_LIST_HEAD(&insn->stack_ops);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000382
383 insn->sec = sec;
384 insn->offset = offset;
385
386 ret = arch_decode_instruction(file->elf, sec, offset,
387 sec->len - offset,
388 &insn->len, &insn->type,
389 &insn->immediate,
Olivier Deprez157378f2022-04-04 15:47:50 +0200390 &insn->stack_ops);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000391 if (ret)
392 goto err;
393
Olivier Deprez157378f2022-04-04 15:47:50 +0200394 hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset));
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000395 list_add_tail(&insn->list, &file->insn_list);
Olivier Deprez157378f2022-04-04 15:47:50 +0200396 nr_insns++;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000397 }
398
399 list_for_each_entry(func, &sec->symbol_list, list) {
David Brazdil0f672f62019-12-10 10:32:29 +0000400 if (func->type != STT_FUNC || func->alias != func)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000401 continue;
402
403 if (!find_insn(file, sec, func->offset)) {
404 WARN("%s(): can't find starting instruction",
405 func->name);
406 return -1;
407 }
408
Olivier Deprez157378f2022-04-04 15:47:50 +0200409 sym_for_each_insn(file, func, insn)
David Brazdil0f672f62019-12-10 10:32:29 +0000410 insn->func = func;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000411 }
412 }
413
Olivier Deprez157378f2022-04-04 15:47:50 +0200414 if (stats)
415 printf("nr_insns: %lu\n", nr_insns);
416
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000417 return 0;
418
419err:
420 free(insn);
421 return ret;
422}
423
Olivier Deprez157378f2022-04-04 15:47:50 +0200424static struct instruction *find_last_insn(struct objtool_file *file,
425 struct section *sec)
426{
427 struct instruction *insn = NULL;
428 unsigned int offset;
429 unsigned int end = (sec->len > 10) ? sec->len - 10 : 0;
430
431 for (offset = sec->len - 1; offset >= end && !insn; offset--)
432 insn = find_insn(file, sec, offset);
433
434 return insn;
435}
436
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000437/*
438 * Mark "ud2" instructions and manually annotated dead ends.
439 */
440static int add_dead_ends(struct objtool_file *file)
441{
442 struct section *sec;
Olivier Deprez157378f2022-04-04 15:47:50 +0200443 struct reloc *reloc;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000444 struct instruction *insn;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000445
446 /*
447 * By default, "ud2" is a dead end unless otherwise annotated, because
448 * GCC 7 inserts it for certain divide-by-zero cases.
449 */
450 for_each_insn(file, insn)
451 if (insn->type == INSN_BUG)
452 insn->dead_end = true;
453
454 /*
455 * Check for manually annotated dead ends.
456 */
457 sec = find_section_by_name(file->elf, ".rela.discard.unreachable");
458 if (!sec)
459 goto reachable;
460
Olivier Deprez157378f2022-04-04 15:47:50 +0200461 list_for_each_entry(reloc, &sec->reloc_list, list) {
462 if (reloc->sym->type != STT_SECTION) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000463 WARN("unexpected relocation symbol type in %s", sec->name);
464 return -1;
465 }
Olivier Deprez157378f2022-04-04 15:47:50 +0200466 insn = find_insn(file, reloc->sym->sec, reloc->addend);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000467 if (insn)
468 insn = list_prev_entry(insn, list);
Olivier Deprez157378f2022-04-04 15:47:50 +0200469 else if (reloc->addend == reloc->sym->sec->len) {
470 insn = find_last_insn(file, reloc->sym->sec);
471 if (!insn) {
Olivier Deprez92d4c212022-12-06 15:05:30 +0100472 WARN("can't find unreachable insn at %s+0x%" PRIx64,
Olivier Deprez157378f2022-04-04 15:47:50 +0200473 reloc->sym->sec->name, reloc->addend);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000474 return -1;
475 }
476 } else {
Olivier Deprez92d4c212022-12-06 15:05:30 +0100477 WARN("can't find unreachable insn at %s+0x%" PRIx64,
Olivier Deprez157378f2022-04-04 15:47:50 +0200478 reloc->sym->sec->name, reloc->addend);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000479 return -1;
480 }
481
482 insn->dead_end = true;
483 }
484
485reachable:
486 /*
487 * These manually annotated reachable checks are needed for GCC 4.4,
488 * where the Linux unreachable() macro isn't supported. In that case
489 * GCC doesn't know the "ud2" is fatal, so it generates code as if it's
490 * not a dead end.
491 */
492 sec = find_section_by_name(file->elf, ".rela.discard.reachable");
493 if (!sec)
494 return 0;
495
Olivier Deprez157378f2022-04-04 15:47:50 +0200496 list_for_each_entry(reloc, &sec->reloc_list, list) {
497 if (reloc->sym->type != STT_SECTION) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000498 WARN("unexpected relocation symbol type in %s", sec->name);
499 return -1;
500 }
Olivier Deprez157378f2022-04-04 15:47:50 +0200501 insn = find_insn(file, reloc->sym->sec, reloc->addend);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000502 if (insn)
503 insn = list_prev_entry(insn, list);
Olivier Deprez157378f2022-04-04 15:47:50 +0200504 else if (reloc->addend == reloc->sym->sec->len) {
505 insn = find_last_insn(file, reloc->sym->sec);
506 if (!insn) {
Olivier Deprez92d4c212022-12-06 15:05:30 +0100507 WARN("can't find reachable insn at %s+0x%" PRIx64,
Olivier Deprez157378f2022-04-04 15:47:50 +0200508 reloc->sym->sec->name, reloc->addend);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000509 return -1;
510 }
511 } else {
Olivier Deprez92d4c212022-12-06 15:05:30 +0100512 WARN("can't find reachable insn at %s+0x%" PRIx64,
Olivier Deprez157378f2022-04-04 15:47:50 +0200513 reloc->sym->sec->name, reloc->addend);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000514 return -1;
515 }
516
517 insn->dead_end = false;
518 }
519
520 return 0;
521}
522
Olivier Deprez157378f2022-04-04 15:47:50 +0200523static int create_static_call_sections(struct objtool_file *file)
524{
Olivier Deprez92d4c212022-12-06 15:05:30 +0100525 struct section *sec;
Olivier Deprez157378f2022-04-04 15:47:50 +0200526 struct static_call_site *site;
527 struct instruction *insn;
528 struct symbol *key_sym;
529 char *key_name, *tmp;
530 int idx;
531
532 sec = find_section_by_name(file->elf, ".static_call_sites");
533 if (sec) {
534 INIT_LIST_HEAD(&file->static_call_list);
535 WARN("file already has .static_call_sites section, skipping");
536 return 0;
537 }
538
539 if (list_empty(&file->static_call_list))
540 return 0;
541
542 idx = 0;
Olivier Deprez92d4c212022-12-06 15:05:30 +0100543 list_for_each_entry(insn, &file->static_call_list, call_node)
Olivier Deprez157378f2022-04-04 15:47:50 +0200544 idx++;
545
546 sec = elf_create_section(file->elf, ".static_call_sites", SHF_WRITE,
547 sizeof(struct static_call_site), idx);
548 if (!sec)
549 return -1;
550
Olivier Deprez157378f2022-04-04 15:47:50 +0200551 idx = 0;
Olivier Deprez92d4c212022-12-06 15:05:30 +0100552 list_for_each_entry(insn, &file->static_call_list, call_node) {
Olivier Deprez157378f2022-04-04 15:47:50 +0200553
554 site = (struct static_call_site *)sec->data->d_buf + idx;
555 memset(site, 0, sizeof(struct static_call_site));
556
557 /* populate reloc for 'addr' */
Olivier Deprez92d4c212022-12-06 15:05:30 +0100558 if (elf_add_reloc_to_insn(file->elf, sec,
559 idx * sizeof(struct static_call_site),
560 R_X86_64_PC32,
561 insn->sec, insn->offset))
Olivier Deprez157378f2022-04-04 15:47:50 +0200562 return -1;
Olivier Deprez157378f2022-04-04 15:47:50 +0200563
564 /* find key symbol */
565 key_name = strdup(insn->call_dest->name);
566 if (!key_name) {
567 perror("strdup");
568 return -1;
569 }
570 if (strncmp(key_name, STATIC_CALL_TRAMP_PREFIX_STR,
571 STATIC_CALL_TRAMP_PREFIX_LEN)) {
572 WARN("static_call: trampoline name malformed: %s", key_name);
573 return -1;
574 }
575 tmp = key_name + STATIC_CALL_TRAMP_PREFIX_LEN - STATIC_CALL_KEY_PREFIX_LEN;
576 memcpy(tmp, STATIC_CALL_KEY_PREFIX_STR, STATIC_CALL_KEY_PREFIX_LEN);
577
578 key_sym = find_symbol_by_name(file->elf, tmp);
579 if (!key_sym) {
580 if (!module) {
581 WARN("static_call: can't find static_call_key symbol: %s", tmp);
582 return -1;
583 }
584
585 /*
586 * For modules(), the key might not be exported, which
587 * means the module can make static calls but isn't
588 * allowed to change them.
589 *
590 * In that case we temporarily set the key to be the
591 * trampoline address. This is fixed up in
592 * static_call_add_module().
593 */
594 key_sym = insn->call_dest;
595 }
596 free(key_name);
597
598 /* populate reloc for 'key' */
Olivier Deprez92d4c212022-12-06 15:05:30 +0100599 if (elf_add_reloc(file->elf, sec,
600 idx * sizeof(struct static_call_site) + 4,
601 R_X86_64_PC32, key_sym,
602 is_sibling_call(insn) * STATIC_CALL_SITE_TAIL))
Olivier Deprez157378f2022-04-04 15:47:50 +0200603 return -1;
Olivier Deprez157378f2022-04-04 15:47:50 +0200604
605 idx++;
606 }
607
Olivier Deprez92d4c212022-12-06 15:05:30 +0100608 return 0;
609}
610
611static int create_retpoline_sites_sections(struct objtool_file *file)
612{
613 struct instruction *insn;
614 struct section *sec;
615 int idx;
616
617 sec = find_section_by_name(file->elf, ".retpoline_sites");
618 if (sec) {
619 WARN("file already has .retpoline_sites, skipping");
620 return 0;
621 }
622
623 idx = 0;
624 list_for_each_entry(insn, &file->retpoline_call_list, call_node)
625 idx++;
626
627 if (!idx)
628 return 0;
629
630 sec = elf_create_section(file->elf, ".retpoline_sites", 0,
631 sizeof(int), idx);
632 if (!sec) {
633 WARN("elf_create_section: .retpoline_sites");
Olivier Deprez157378f2022-04-04 15:47:50 +0200634 return -1;
Olivier Deprez92d4c212022-12-06 15:05:30 +0100635 }
636
637 idx = 0;
638 list_for_each_entry(insn, &file->retpoline_call_list, call_node) {
639
640 int *site = (int *)sec->data->d_buf + idx;
641 *site = 0;
642
643 if (elf_add_reloc_to_insn(file->elf, sec,
644 idx * sizeof(int),
645 R_X86_64_PC32,
646 insn->sec, insn->offset)) {
647 WARN("elf_add_reloc_to_insn: .retpoline_sites");
648 return -1;
649 }
650
651 idx++;
652 }
653
654 return 0;
655}
656
657static int create_return_sites_sections(struct objtool_file *file)
658{
659 struct instruction *insn;
660 struct section *sec;
661 int idx;
662
663 sec = find_section_by_name(file->elf, ".return_sites");
664 if (sec) {
665 WARN("file already has .return_sites, skipping");
666 return 0;
667 }
668
669 idx = 0;
670 list_for_each_entry(insn, &file->return_thunk_list, call_node)
671 idx++;
672
673 if (!idx)
674 return 0;
675
676 sec = elf_create_section(file->elf, ".return_sites", 0,
677 sizeof(int), idx);
678 if (!sec) {
679 WARN("elf_create_section: .return_sites");
680 return -1;
681 }
682
683 idx = 0;
684 list_for_each_entry(insn, &file->return_thunk_list, call_node) {
685
686 int *site = (int *)sec->data->d_buf + idx;
687 *site = 0;
688
689 if (elf_add_reloc_to_insn(file->elf, sec,
690 idx * sizeof(int),
691 R_X86_64_PC32,
692 insn->sec, insn->offset)) {
693 WARN("elf_add_reloc_to_insn: .return_sites");
694 return -1;
695 }
696
697 idx++;
698 }
Olivier Deprez157378f2022-04-04 15:47:50 +0200699
700 return 0;
701}
702
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000703/*
704 * Warnings shouldn't be reported for ignored functions.
705 */
706static void add_ignores(struct objtool_file *file)
707{
708 struct instruction *insn;
709 struct section *sec;
710 struct symbol *func;
Olivier Deprez157378f2022-04-04 15:47:50 +0200711 struct reloc *reloc;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000712
David Brazdil0f672f62019-12-10 10:32:29 +0000713 sec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard");
714 if (!sec)
715 return;
716
Olivier Deprez157378f2022-04-04 15:47:50 +0200717 list_for_each_entry(reloc, &sec->reloc_list, list) {
718 switch (reloc->sym->type) {
David Brazdil0f672f62019-12-10 10:32:29 +0000719 case STT_FUNC:
Olivier Deprez157378f2022-04-04 15:47:50 +0200720 func = reloc->sym;
David Brazdil0f672f62019-12-10 10:32:29 +0000721 break;
722
723 case STT_SECTION:
Olivier Deprez157378f2022-04-04 15:47:50 +0200724 func = find_func_by_offset(reloc->sym->sec, reloc->addend);
725 if (!func)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000726 continue;
David Brazdil0f672f62019-12-10 10:32:29 +0000727 break;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000728
David Brazdil0f672f62019-12-10 10:32:29 +0000729 default:
Olivier Deprez157378f2022-04-04 15:47:50 +0200730 WARN("unexpected relocation symbol type in %s: %d", sec->name, reloc->sym->type);
David Brazdil0f672f62019-12-10 10:32:29 +0000731 continue;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000732 }
David Brazdil0f672f62019-12-10 10:32:29 +0000733
Olivier Deprez157378f2022-04-04 15:47:50 +0200734 func_for_each_insn(file, func, insn)
David Brazdil0f672f62019-12-10 10:32:29 +0000735 insn->ignore = true;
736 }
737}
738
739/*
740 * This is a whitelist of functions that is allowed to be called with AC set.
741 * The list is meant to be minimal and only contains compiler instrumentation
742 * ABI and a few functions used to implement *_{to,from}_user() functions.
743 *
744 * These functions must not directly change AC, but may PUSHF/POPF.
745 */
746static const char *uaccess_safe_builtin[] = {
747 /* KASAN */
748 "kasan_report",
749 "check_memory_region",
750 /* KASAN out-of-line */
751 "__asan_loadN_noabort",
752 "__asan_load1_noabort",
753 "__asan_load2_noabort",
754 "__asan_load4_noabort",
755 "__asan_load8_noabort",
756 "__asan_load16_noabort",
757 "__asan_storeN_noabort",
758 "__asan_store1_noabort",
759 "__asan_store2_noabort",
760 "__asan_store4_noabort",
761 "__asan_store8_noabort",
762 "__asan_store16_noabort",
Olivier Deprez157378f2022-04-04 15:47:50 +0200763 "__kasan_check_read",
764 "__kasan_check_write",
David Brazdil0f672f62019-12-10 10:32:29 +0000765 /* KASAN in-line */
766 "__asan_report_load_n_noabort",
767 "__asan_report_load1_noabort",
768 "__asan_report_load2_noabort",
769 "__asan_report_load4_noabort",
770 "__asan_report_load8_noabort",
771 "__asan_report_load16_noabort",
772 "__asan_report_store_n_noabort",
773 "__asan_report_store1_noabort",
774 "__asan_report_store2_noabort",
775 "__asan_report_store4_noabort",
776 "__asan_report_store8_noabort",
777 "__asan_report_store16_noabort",
Olivier Deprez157378f2022-04-04 15:47:50 +0200778 /* KCSAN */
779 "__kcsan_check_access",
780 "kcsan_found_watchpoint",
781 "kcsan_setup_watchpoint",
782 "kcsan_check_scoped_accesses",
783 "kcsan_disable_current",
784 "kcsan_enable_current_nowarn",
785 /* KCSAN/TSAN */
786 "__tsan_func_entry",
787 "__tsan_func_exit",
788 "__tsan_read_range",
789 "__tsan_write_range",
790 "__tsan_read1",
791 "__tsan_read2",
792 "__tsan_read4",
793 "__tsan_read8",
794 "__tsan_read16",
795 "__tsan_write1",
796 "__tsan_write2",
797 "__tsan_write4",
798 "__tsan_write8",
799 "__tsan_write16",
800 "__tsan_read_write1",
801 "__tsan_read_write2",
802 "__tsan_read_write4",
803 "__tsan_read_write8",
804 "__tsan_read_write16",
805 "__tsan_atomic8_load",
806 "__tsan_atomic16_load",
807 "__tsan_atomic32_load",
808 "__tsan_atomic64_load",
809 "__tsan_atomic8_store",
810 "__tsan_atomic16_store",
811 "__tsan_atomic32_store",
812 "__tsan_atomic64_store",
813 "__tsan_atomic8_exchange",
814 "__tsan_atomic16_exchange",
815 "__tsan_atomic32_exchange",
816 "__tsan_atomic64_exchange",
817 "__tsan_atomic8_fetch_add",
818 "__tsan_atomic16_fetch_add",
819 "__tsan_atomic32_fetch_add",
820 "__tsan_atomic64_fetch_add",
821 "__tsan_atomic8_fetch_sub",
822 "__tsan_atomic16_fetch_sub",
823 "__tsan_atomic32_fetch_sub",
824 "__tsan_atomic64_fetch_sub",
825 "__tsan_atomic8_fetch_and",
826 "__tsan_atomic16_fetch_and",
827 "__tsan_atomic32_fetch_and",
828 "__tsan_atomic64_fetch_and",
829 "__tsan_atomic8_fetch_or",
830 "__tsan_atomic16_fetch_or",
831 "__tsan_atomic32_fetch_or",
832 "__tsan_atomic64_fetch_or",
833 "__tsan_atomic8_fetch_xor",
834 "__tsan_atomic16_fetch_xor",
835 "__tsan_atomic32_fetch_xor",
836 "__tsan_atomic64_fetch_xor",
837 "__tsan_atomic8_fetch_nand",
838 "__tsan_atomic16_fetch_nand",
839 "__tsan_atomic32_fetch_nand",
840 "__tsan_atomic64_fetch_nand",
841 "__tsan_atomic8_compare_exchange_strong",
842 "__tsan_atomic16_compare_exchange_strong",
843 "__tsan_atomic32_compare_exchange_strong",
844 "__tsan_atomic64_compare_exchange_strong",
845 "__tsan_atomic8_compare_exchange_weak",
846 "__tsan_atomic16_compare_exchange_weak",
847 "__tsan_atomic32_compare_exchange_weak",
848 "__tsan_atomic64_compare_exchange_weak",
849 "__tsan_atomic8_compare_exchange_val",
850 "__tsan_atomic16_compare_exchange_val",
851 "__tsan_atomic32_compare_exchange_val",
852 "__tsan_atomic64_compare_exchange_val",
853 "__tsan_atomic_thread_fence",
854 "__tsan_atomic_signal_fence",
David Brazdil0f672f62019-12-10 10:32:29 +0000855 /* KCOV */
856 "write_comp_data",
Olivier Deprez157378f2022-04-04 15:47:50 +0200857 "check_kcov_mode",
David Brazdil0f672f62019-12-10 10:32:29 +0000858 "__sanitizer_cov_trace_pc",
859 "__sanitizer_cov_trace_const_cmp1",
860 "__sanitizer_cov_trace_const_cmp2",
861 "__sanitizer_cov_trace_const_cmp4",
862 "__sanitizer_cov_trace_const_cmp8",
863 "__sanitizer_cov_trace_cmp1",
864 "__sanitizer_cov_trace_cmp2",
865 "__sanitizer_cov_trace_cmp4",
866 "__sanitizer_cov_trace_cmp8",
Olivier Deprez157378f2022-04-04 15:47:50 +0200867 "__sanitizer_cov_trace_switch",
David Brazdil0f672f62019-12-10 10:32:29 +0000868 /* UBSAN */
869 "ubsan_type_mismatch_common",
870 "__ubsan_handle_type_mismatch",
871 "__ubsan_handle_type_mismatch_v1",
Olivier Deprez0e641232021-09-23 10:07:05 +0200872 "__ubsan_handle_shift_out_of_bounds",
David Brazdil0f672f62019-12-10 10:32:29 +0000873 /* misc */
874 "csum_partial_copy_generic",
Olivier Deprez157378f2022-04-04 15:47:50 +0200875 "copy_mc_fragile",
876 "copy_mc_fragile_handle_tail",
877 "copy_mc_enhanced_fast_string",
David Brazdil0f672f62019-12-10 10:32:29 +0000878 "ftrace_likely_update", /* CONFIG_TRACE_BRANCH_PROFILING */
879 NULL
880};
881
882static void add_uaccess_safe(struct objtool_file *file)
883{
884 struct symbol *func;
885 const char **name;
886
887 if (!uaccess)
888 return;
889
890 for (name = uaccess_safe_builtin; *name; name++) {
891 func = find_symbol_by_name(file->elf, *name);
892 if (!func)
893 continue;
894
895 func->uaccess_safe = true;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000896 }
897}
898
899/*
900 * FIXME: For now, just ignore any alternatives which add retpolines. This is
901 * a temporary hack, as it doesn't allow ORC to unwind from inside a retpoline.
902 * But it at least allows objtool to understand the control flow *around* the
903 * retpoline.
904 */
David Brazdil0f672f62019-12-10 10:32:29 +0000905static int add_ignore_alternatives(struct objtool_file *file)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000906{
907 struct section *sec;
Olivier Deprez157378f2022-04-04 15:47:50 +0200908 struct reloc *reloc;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000909 struct instruction *insn;
910
David Brazdil0f672f62019-12-10 10:32:29 +0000911 sec = find_section_by_name(file->elf, ".rela.discard.ignore_alts");
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000912 if (!sec)
913 return 0;
914
Olivier Deprez157378f2022-04-04 15:47:50 +0200915 list_for_each_entry(reloc, &sec->reloc_list, list) {
916 if (reloc->sym->type != STT_SECTION) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000917 WARN("unexpected relocation symbol type in %s", sec->name);
918 return -1;
919 }
920
Olivier Deprez157378f2022-04-04 15:47:50 +0200921 insn = find_insn(file, reloc->sym->sec, reloc->addend);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000922 if (!insn) {
David Brazdil0f672f62019-12-10 10:32:29 +0000923 WARN("bad .discard.ignore_alts entry");
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000924 return -1;
925 }
926
927 insn->ignore_alts = true;
928 }
929
930 return 0;
931}
932
Olivier Deprez92d4c212022-12-06 15:05:30 +0100933__weak bool arch_is_retpoline(struct symbol *sym)
934{
935 return false;
936}
937
938__weak bool arch_is_rethunk(struct symbol *sym)
939{
940 return false;
941}
942
943#define NEGATIVE_RELOC ((void *)-1L)
944
945static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn)
946{
947 if (insn->reloc == NEGATIVE_RELOC)
948 return NULL;
949
950 if (!insn->reloc) {
951 insn->reloc = find_reloc_by_dest_range(file->elf, insn->sec,
952 insn->offset, insn->len);
953 if (!insn->reloc) {
954 insn->reloc = NEGATIVE_RELOC;
955 return NULL;
956 }
957 }
958
959 return insn->reloc;
960}
961
962static void remove_insn_ops(struct instruction *insn)
963{
964 struct stack_op *op, *tmp;
965
966 list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) {
967 list_del(&op->list);
968 free(op);
969 }
970}
971
972static void annotate_call_site(struct objtool_file *file,
973 struct instruction *insn, bool sibling)
974{
975 struct reloc *reloc = insn_reloc(file, insn);
976 struct symbol *sym = insn->call_dest;
977
978 if (!sym)
979 sym = reloc->sym;
980
981 /*
982 * Alternative replacement code is just template code which is
983 * sometimes copied to the original instruction. For now, don't
984 * annotate it. (In the future we might consider annotating the
985 * original instruction if/when it ever makes sense to do so.)
986 */
987 if (!strcmp(insn->sec->name, ".altinstr_replacement"))
988 return;
989
990 if (sym->static_call_tramp) {
991 list_add_tail(&insn->call_node, &file->static_call_list);
992 return;
993 }
994
995 if (sym->retpoline_thunk) {
996 list_add_tail(&insn->call_node, &file->retpoline_call_list);
997 return;
998 }
999
1000 /*
1001 * Many compilers cannot disable KCOV with a function attribute
1002 * so they need a little help, NOP out any KCOV calls from noinstr
1003 * text.
1004 */
1005 if (insn->sec->noinstr && sym->kcov) {
1006 if (reloc) {
1007 reloc->type = R_NONE;
1008 elf_write_reloc(file->elf, reloc);
1009 }
1010
1011 elf_write_insn(file->elf, insn->sec,
1012 insn->offset, insn->len,
1013 sibling ? arch_ret_insn(insn->len)
1014 : arch_nop_insn(insn->len));
1015
1016 insn->type = sibling ? INSN_RETURN : INSN_NOP;
1017
1018 if (sibling) {
1019 /*
1020 * We've replaced the tail-call JMP insn by two new
1021 * insn: RET; INT3, except we only have a single struct
1022 * insn here. Mark it retpoline_safe to avoid the SLS
1023 * warning, instead of adding another insn.
1024 */
1025 insn->retpoline_safe = true;
1026 }
1027
1028 return;
1029 }
1030}
1031
1032static void add_call_dest(struct objtool_file *file, struct instruction *insn,
1033 struct symbol *dest, bool sibling)
1034{
1035 insn->call_dest = dest;
1036 if (!dest)
1037 return;
1038
1039 /*
1040 * Whatever stack impact regular CALLs have, should be undone
1041 * by the RETURN of the called function.
1042 *
1043 * Annotated intra-function calls retain the stack_ops but
1044 * are converted to JUMP, see read_intra_function_calls().
1045 */
1046 remove_insn_ops(insn);
1047
1048 annotate_call_site(file, insn, sibling);
1049}
1050
1051static void add_retpoline_call(struct objtool_file *file, struct instruction *insn)
1052{
1053 /*
1054 * Retpoline calls/jumps are really dynamic calls/jumps in disguise,
1055 * so convert them accordingly.
1056 */
1057 switch (insn->type) {
1058 case INSN_CALL:
1059 insn->type = INSN_CALL_DYNAMIC;
1060 break;
1061 case INSN_JUMP_UNCONDITIONAL:
1062 insn->type = INSN_JUMP_DYNAMIC;
1063 break;
1064 case INSN_JUMP_CONDITIONAL:
1065 insn->type = INSN_JUMP_DYNAMIC_CONDITIONAL;
1066 break;
1067 default:
1068 return;
1069 }
1070
1071 insn->retpoline_safe = true;
1072
1073 /*
1074 * Whatever stack impact regular CALLs have, should be undone
1075 * by the RETURN of the called function.
1076 *
1077 * Annotated intra-function calls retain the stack_ops but
1078 * are converted to JUMP, see read_intra_function_calls().
1079 */
1080 remove_insn_ops(insn);
1081
1082 annotate_call_site(file, insn, false);
1083}
1084
1085static void add_return_call(struct objtool_file *file, struct instruction *insn, bool add)
1086{
1087 /*
1088 * Return thunk tail calls are really just returns in disguise,
1089 * so convert them accordingly.
1090 */
1091 insn->type = INSN_RETURN;
1092 insn->retpoline_safe = true;
1093
1094 /* Skip the non-text sections, specially .discard ones */
1095 if (add && insn->sec->text)
1096 list_add_tail(&insn->call_node, &file->return_thunk_list);
1097}
1098
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001099/*
1100 * Find the destination instructions for all jumps.
1101 */
1102static int add_jump_destinations(struct objtool_file *file)
1103{
1104 struct instruction *insn;
Olivier Deprez157378f2022-04-04 15:47:50 +02001105 struct reloc *reloc;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001106 struct section *dest_sec;
1107 unsigned long dest_off;
1108
1109 for_each_insn(file, insn) {
Olivier Deprez157378f2022-04-04 15:47:50 +02001110 if (!is_static_jump(insn))
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001111 continue;
1112
Olivier Deprez92d4c212022-12-06 15:05:30 +01001113 reloc = insn_reloc(file, insn);
Olivier Deprez157378f2022-04-04 15:47:50 +02001114 if (!reloc) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001115 dest_sec = insn->sec;
Olivier Deprez157378f2022-04-04 15:47:50 +02001116 dest_off = arch_jump_destination(insn);
1117 } else if (reloc->sym->type == STT_SECTION) {
1118 dest_sec = reloc->sym->sec;
1119 dest_off = arch_dest_reloc_offset(reloc->addend);
Olivier Deprez92d4c212022-12-06 15:05:30 +01001120 } else if (reloc->sym->retpoline_thunk) {
1121 add_retpoline_call(file, insn);
1122 continue;
1123 } else if (reloc->sym->return_thunk) {
1124 add_return_call(file, insn, true);
1125 continue;
1126 } else if (insn->func) {
1127 /* internal or external sibling call (with reloc) */
1128 add_call_dest(file, insn, reloc->sym, true);
1129 continue;
Olivier Deprez157378f2022-04-04 15:47:50 +02001130 } else if (reloc->sym->sec->idx) {
1131 dest_sec = reloc->sym->sec;
1132 dest_off = reloc->sym->sym.st_value +
1133 arch_dest_reloc_offset(reloc->addend);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001134 } else {
Olivier Deprez92d4c212022-12-06 15:05:30 +01001135 /* non-func asm code jumping to another file */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001136 continue;
1137 }
1138
1139 insn->jump_dest = find_insn(file, dest_sec, dest_off);
1140 if (!insn->jump_dest) {
Olivier Deprez92d4c212022-12-06 15:05:30 +01001141 struct symbol *sym = find_symbol_by_offset(dest_sec, dest_off);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001142
1143 /*
1144 * This is a special case where an alt instruction
1145 * jumps past the end of the section. These are
1146 * handled later in handle_group_alt().
1147 */
1148 if (!strcmp(insn->sec->name, ".altinstr_replacement"))
1149 continue;
1150
Olivier Deprez92d4c212022-12-06 15:05:30 +01001151 /*
1152 * This is a special case for zen_untrain_ret().
1153 * It jumps to __x86_return_thunk(), but objtool
1154 * can't find the thunk's starting RET
1155 * instruction, because the RET is also in the
1156 * middle of another instruction. Objtool only
1157 * knows about the outer instruction.
1158 */
1159 if (sym && sym->return_thunk) {
1160 add_return_call(file, insn, false);
1161 continue;
1162 }
1163
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001164 WARN_FUNC("can't find jump dest instruction at %s+0x%lx",
1165 insn->sec, insn->offset, dest_sec->name,
1166 dest_off);
1167 return -1;
1168 }
1169
1170 /*
David Brazdil0f672f62019-12-10 10:32:29 +00001171 * Cross-function jump.
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001172 */
1173 if (insn->func && insn->jump_dest->func &&
David Brazdil0f672f62019-12-10 10:32:29 +00001174 insn->func != insn->jump_dest->func) {
1175
1176 /*
1177 * For GCC 8+, create parent/child links for any cold
1178 * subfunctions. This is _mostly_ redundant with a
1179 * similar initialization in read_symbols().
1180 *
1181 * If a function has aliases, we want the *first* such
1182 * function in the symbol table to be the subfunction's
1183 * parent. In that case we overwrite the
1184 * initialization done in read_symbols().
1185 *
1186 * However this code can't completely replace the
1187 * read_symbols() code because this doesn't detect the
1188 * case where the parent function's only reference to a
1189 * subfunction is through a jump table.
1190 */
Olivier Deprez0e641232021-09-23 10:07:05 +02001191 if (!strstr(insn->func->name, ".cold") &&
1192 strstr(insn->jump_dest->func->name, ".cold")) {
David Brazdil0f672f62019-12-10 10:32:29 +00001193 insn->func->cfunc = insn->jump_dest->func;
1194 insn->jump_dest->func->pfunc = insn->func;
1195
1196 } else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
1197 insn->jump_dest->offset == insn->jump_dest->func->offset) {
Olivier Deprez92d4c212022-12-06 15:05:30 +01001198 /* internal sibling call (without reloc) */
1199 add_call_dest(file, insn, insn->jump_dest->func, true);
David Brazdil0f672f62019-12-10 10:32:29 +00001200 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001201 }
1202 }
1203
1204 return 0;
1205}
1206
Olivier Deprez157378f2022-04-04 15:47:50 +02001207static struct symbol *find_call_destination(struct section *sec, unsigned long offset)
1208{
1209 struct symbol *call_dest;
1210
1211 call_dest = find_func_by_offset(sec, offset);
1212 if (!call_dest)
1213 call_dest = find_symbol_by_offset(sec, offset);
1214
1215 return call_dest;
1216}
1217
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001218/*
1219 * Find the destination instructions for all calls.
1220 */
1221static int add_call_destinations(struct objtool_file *file)
1222{
1223 struct instruction *insn;
1224 unsigned long dest_off;
Olivier Deprez92d4c212022-12-06 15:05:30 +01001225 struct symbol *dest;
Olivier Deprez157378f2022-04-04 15:47:50 +02001226 struct reloc *reloc;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001227
1228 for_each_insn(file, insn) {
1229 if (insn->type != INSN_CALL)
1230 continue;
1231
Olivier Deprez92d4c212022-12-06 15:05:30 +01001232 reloc = insn_reloc(file, insn);
Olivier Deprez157378f2022-04-04 15:47:50 +02001233 if (!reloc) {
1234 dest_off = arch_jump_destination(insn);
Olivier Deprez92d4c212022-12-06 15:05:30 +01001235 dest = find_call_destination(insn->sec, dest_off);
1236
1237 add_call_dest(file, insn, dest, false);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001238
Olivier Deprez157378f2022-04-04 15:47:50 +02001239 if (insn->ignore)
1240 continue;
1241
1242 if (!insn->call_dest) {
1243 WARN_FUNC("unannotated intra-function call", insn->sec, insn->offset);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001244 return -1;
1245 }
1246
Olivier Deprez157378f2022-04-04 15:47:50 +02001247 if (insn->func && insn->call_dest->type != STT_FUNC) {
1248 WARN_FUNC("unsupported call to non-function",
1249 insn->sec, insn->offset);
1250 return -1;
1251 }
1252
1253 } else if (reloc->sym->type == STT_SECTION) {
1254 dest_off = arch_dest_reloc_offset(reloc->addend);
Olivier Deprez92d4c212022-12-06 15:05:30 +01001255 dest = find_call_destination(reloc->sym->sec, dest_off);
1256 if (!dest) {
Olivier Deprez157378f2022-04-04 15:47:50 +02001257 WARN_FUNC("can't find call dest symbol at %s+0x%lx",
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001258 insn->sec, insn->offset,
Olivier Deprez157378f2022-04-04 15:47:50 +02001259 reloc->sym->sec->name,
1260 dest_off);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001261 return -1;
1262 }
Olivier Deprez92d4c212022-12-06 15:05:30 +01001263
1264 add_call_dest(file, insn, dest, false);
1265
1266 } else if (reloc->sym->retpoline_thunk) {
1267 add_retpoline_call(file, insn);
1268
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001269 } else
Olivier Deprez92d4c212022-12-06 15:05:30 +01001270 add_call_dest(file, insn, reloc->sym, false);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001271 }
1272
1273 return 0;
1274}
1275
1276/*
Olivier Deprez92d4c212022-12-06 15:05:30 +01001277 * The .alternatives section requires some extra special care over and above
1278 * other special sections because alternatives are patched in place.
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001279 */
1280static int handle_group_alt(struct objtool_file *file,
1281 struct special_alt *special_alt,
1282 struct instruction *orig_insn,
1283 struct instruction **new_insn)
1284{
Olivier Deprez92d4c212022-12-06 15:05:30 +01001285 struct instruction *last_orig_insn, *last_new_insn = NULL, *insn, *nop = NULL;
1286 struct alt_group *orig_alt_group, *new_alt_group;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001287 unsigned long dest_off;
1288
Olivier Deprez92d4c212022-12-06 15:05:30 +01001289
1290 orig_alt_group = malloc(sizeof(*orig_alt_group));
1291 if (!orig_alt_group) {
1292 WARN("malloc failed");
1293 return -1;
1294 }
1295 orig_alt_group->cfi = calloc(special_alt->orig_len,
1296 sizeof(struct cfi_state *));
1297 if (!orig_alt_group->cfi) {
1298 WARN("calloc failed");
1299 return -1;
1300 }
1301
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001302 last_orig_insn = NULL;
1303 insn = orig_insn;
1304 sec_for_each_insn_from(file, insn) {
1305 if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
1306 break;
1307
Olivier Deprez92d4c212022-12-06 15:05:30 +01001308 insn->alt_group = orig_alt_group;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001309 last_orig_insn = insn;
1310 }
Olivier Deprez92d4c212022-12-06 15:05:30 +01001311 orig_alt_group->orig_group = NULL;
1312 orig_alt_group->first_insn = orig_insn;
1313 orig_alt_group->last_insn = last_orig_insn;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001314
Olivier Deprez92d4c212022-12-06 15:05:30 +01001315
1316 new_alt_group = malloc(sizeof(*new_alt_group));
1317 if (!new_alt_group) {
1318 WARN("malloc failed");
1319 return -1;
1320 }
1321
1322 if (special_alt->new_len < special_alt->orig_len) {
1323 /*
1324 * Insert a fake nop at the end to make the replacement
1325 * alt_group the same size as the original. This is needed to
1326 * allow propagate_alt_cfi() to do its magic. When the last
1327 * instruction affects the stack, the instruction after it (the
1328 * nop) will propagate the new state to the shared CFI array.
1329 */
1330 nop = malloc(sizeof(*nop));
1331 if (!nop) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001332 WARN("malloc failed");
1333 return -1;
1334 }
Olivier Deprez92d4c212022-12-06 15:05:30 +01001335 memset(nop, 0, sizeof(*nop));
1336 INIT_LIST_HEAD(&nop->alts);
1337 INIT_LIST_HEAD(&nop->stack_ops);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001338
Olivier Deprez92d4c212022-12-06 15:05:30 +01001339 nop->sec = special_alt->new_sec;
1340 nop->offset = special_alt->new_off + special_alt->new_len;
1341 nop->len = special_alt->orig_len - special_alt->new_len;
1342 nop->type = INSN_NOP;
1343 nop->func = orig_insn->func;
1344 nop->alt_group = new_alt_group;
1345 nop->ignore = orig_insn->ignore_alts;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001346 }
1347
1348 if (!special_alt->new_len) {
Olivier Deprez92d4c212022-12-06 15:05:30 +01001349 *new_insn = nop;
1350 goto end;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001351 }
1352
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001353 insn = *new_insn;
1354 sec_for_each_insn_from(file, insn) {
Olivier Deprez157378f2022-04-04 15:47:50 +02001355 struct reloc *alt_reloc;
1356
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001357 if (insn->offset >= special_alt->new_off + special_alt->new_len)
1358 break;
1359
1360 last_new_insn = insn;
1361
1362 insn->ignore = orig_insn->ignore_alts;
David Brazdil0f672f62019-12-10 10:32:29 +00001363 insn->func = orig_insn->func;
Olivier Deprez92d4c212022-12-06 15:05:30 +01001364 insn->alt_group = new_alt_group;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001365
Olivier Deprez157378f2022-04-04 15:47:50 +02001366 /*
1367 * Since alternative replacement code is copy/pasted by the
1368 * kernel after applying relocations, generally such code can't
1369 * have relative-address relocation references to outside the
1370 * .altinstr_replacement section, unless the arch's
1371 * alternatives code can adjust the relative offsets
1372 * accordingly.
1373 */
Olivier Deprez92d4c212022-12-06 15:05:30 +01001374 alt_reloc = insn_reloc(file, insn);
Olivier Deprez157378f2022-04-04 15:47:50 +02001375 if (alt_reloc &&
1376 !arch_support_alt_relocation(special_alt, insn, alt_reloc)) {
1377
1378 WARN_FUNC("unsupported relocation in alternatives section",
1379 insn->sec, insn->offset);
1380 return -1;
1381 }
1382
1383 if (!is_static_jump(insn))
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001384 continue;
1385
1386 if (!insn->immediate)
1387 continue;
1388
Olivier Deprez157378f2022-04-04 15:47:50 +02001389 dest_off = arch_jump_destination(insn);
Olivier Deprez92d4c212022-12-06 15:05:30 +01001390 if (dest_off == special_alt->new_off + special_alt->new_len)
1391 insn->jump_dest = next_insn_same_sec(file, last_orig_insn);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001392
1393 if (!insn->jump_dest) {
1394 WARN_FUNC("can't find alternative jump destination",
1395 insn->sec, insn->offset);
1396 return -1;
1397 }
1398 }
1399
1400 if (!last_new_insn) {
1401 WARN_FUNC("can't find last new alternative instruction",
1402 special_alt->new_sec, special_alt->new_off);
1403 return -1;
1404 }
1405
Olivier Deprez92d4c212022-12-06 15:05:30 +01001406 if (nop)
1407 list_add(&nop->list, &last_new_insn->list);
1408end:
1409 new_alt_group->orig_group = orig_alt_group;
1410 new_alt_group->first_insn = *new_insn;
1411 new_alt_group->last_insn = nop ? : last_new_insn;
1412 new_alt_group->cfi = orig_alt_group->cfi;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001413 return 0;
1414}
1415
1416/*
1417 * A jump table entry can either convert a nop to a jump or a jump to a nop.
1418 * If the original instruction is a jump, make the alt entry an effective nop
1419 * by just skipping the original instruction.
1420 */
1421static int handle_jump_alt(struct objtool_file *file,
1422 struct special_alt *special_alt,
1423 struct instruction *orig_insn,
1424 struct instruction **new_insn)
1425{
1426 if (orig_insn->type == INSN_NOP)
1427 return 0;
1428
1429 if (orig_insn->type != INSN_JUMP_UNCONDITIONAL) {
1430 WARN_FUNC("unsupported instruction at jump label",
1431 orig_insn->sec, orig_insn->offset);
1432 return -1;
1433 }
1434
1435 *new_insn = list_next_entry(orig_insn, list);
1436 return 0;
1437}
1438
1439/*
1440 * Read all the special sections which have alternate instructions which can be
1441 * patched in or redirected to at runtime. Each instruction having alternate
1442 * instruction(s) has them added to its insn->alts list, which will be
1443 * traversed in validate_branch().
1444 */
1445static int add_special_section_alts(struct objtool_file *file)
1446{
1447 struct list_head special_alts;
1448 struct instruction *orig_insn, *new_insn;
1449 struct special_alt *special_alt, *tmp;
1450 struct alternative *alt;
1451 int ret;
1452
1453 ret = special_get_alts(file->elf, &special_alts);
1454 if (ret)
1455 return ret;
1456
1457 list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {
1458
1459 orig_insn = find_insn(file, special_alt->orig_sec,
1460 special_alt->orig_off);
1461 if (!orig_insn) {
1462 WARN_FUNC("special: can't find orig instruction",
1463 special_alt->orig_sec, special_alt->orig_off);
1464 ret = -1;
1465 goto out;
1466 }
1467
1468 new_insn = NULL;
1469 if (!special_alt->group || special_alt->new_len) {
1470 new_insn = find_insn(file, special_alt->new_sec,
1471 special_alt->new_off);
1472 if (!new_insn) {
1473 WARN_FUNC("special: can't find new instruction",
1474 special_alt->new_sec,
1475 special_alt->new_off);
1476 ret = -1;
1477 goto out;
1478 }
1479 }
1480
1481 if (special_alt->group) {
Olivier Deprez0e641232021-09-23 10:07:05 +02001482 if (!special_alt->orig_len) {
1483 WARN_FUNC("empty alternative entry",
1484 orig_insn->sec, orig_insn->offset);
1485 continue;
1486 }
1487
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001488 ret = handle_group_alt(file, special_alt, orig_insn,
1489 &new_insn);
1490 if (ret)
1491 goto out;
1492 } else if (special_alt->jump_or_nop) {
1493 ret = handle_jump_alt(file, special_alt, orig_insn,
1494 &new_insn);
1495 if (ret)
1496 goto out;
1497 }
1498
1499 alt = malloc(sizeof(*alt));
1500 if (!alt) {
1501 WARN("malloc failed");
1502 ret = -1;
1503 goto out;
1504 }
1505
1506 alt->insn = new_insn;
David Brazdil0f672f62019-12-10 10:32:29 +00001507 alt->skip_orig = special_alt->skip_orig;
1508 orig_insn->ignore_alts |= special_alt->skip_alt;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001509 list_add_tail(&alt->list, &orig_insn->alts);
1510
1511 list_del(&special_alt->list);
1512 free(special_alt);
1513 }
1514
1515out:
1516 return ret;
1517}
1518
David Brazdil0f672f62019-12-10 10:32:29 +00001519static int add_jump_table(struct objtool_file *file, struct instruction *insn,
Olivier Deprez157378f2022-04-04 15:47:50 +02001520 struct reloc *table)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001521{
Olivier Deprez157378f2022-04-04 15:47:50 +02001522 struct reloc *reloc = table;
David Brazdil0f672f62019-12-10 10:32:29 +00001523 struct instruction *dest_insn;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001524 struct alternative *alt;
1525 struct symbol *pfunc = insn->func->pfunc;
1526 unsigned int prev_offset = 0;
1527
David Brazdil0f672f62019-12-10 10:32:29 +00001528 /*
Olivier Deprez157378f2022-04-04 15:47:50 +02001529 * Each @reloc is a switch table relocation which points to the target
David Brazdil0f672f62019-12-10 10:32:29 +00001530 * instruction.
1531 */
Olivier Deprez157378f2022-04-04 15:47:50 +02001532 list_for_each_entry_from(reloc, &table->sec->reloc_list, list) {
David Brazdil0f672f62019-12-10 10:32:29 +00001533
1534 /* Check for the end of the table: */
Olivier Deprez157378f2022-04-04 15:47:50 +02001535 if (reloc != table && reloc->jump_table_start)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001536 break;
1537
David Brazdil0f672f62019-12-10 10:32:29 +00001538 /* Make sure the table entries are consecutive: */
Olivier Deprez157378f2022-04-04 15:47:50 +02001539 if (prev_offset && reloc->offset != prev_offset + 8)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001540 break;
1541
1542 /* Detect function pointers from contiguous objects: */
Olivier Deprez157378f2022-04-04 15:47:50 +02001543 if (reloc->sym->sec == pfunc->sec &&
1544 reloc->addend == pfunc->offset)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001545 break;
1546
Olivier Deprez157378f2022-04-04 15:47:50 +02001547 dest_insn = find_insn(file, reloc->sym->sec, reloc->addend);
David Brazdil0f672f62019-12-10 10:32:29 +00001548 if (!dest_insn)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001549 break;
1550
David Brazdil0f672f62019-12-10 10:32:29 +00001551 /* Make sure the destination is in the same function: */
1552 if (!dest_insn->func || dest_insn->func->pfunc != pfunc)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001553 break;
1554
1555 alt = malloc(sizeof(*alt));
1556 if (!alt) {
1557 WARN("malloc failed");
1558 return -1;
1559 }
1560
David Brazdil0f672f62019-12-10 10:32:29 +00001561 alt->insn = dest_insn;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001562 list_add_tail(&alt->list, &insn->alts);
Olivier Deprez157378f2022-04-04 15:47:50 +02001563 prev_offset = reloc->offset;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001564 }
1565
1566 if (!prev_offset) {
1567 WARN_FUNC("can't find switch jump table",
1568 insn->sec, insn->offset);
1569 return -1;
1570 }
1571
1572 return 0;
1573}
1574
1575/*
Olivier Deprez157378f2022-04-04 15:47:50 +02001576 * find_jump_table() - Given a dynamic jump, find the switch jump table
1577 * associated with it.
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001578 */
Olivier Deprez157378f2022-04-04 15:47:50 +02001579static struct reloc *find_jump_table(struct objtool_file *file,
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001580 struct symbol *func,
1581 struct instruction *insn)
1582{
Olivier Deprez157378f2022-04-04 15:47:50 +02001583 struct reloc *table_reloc;
1584 struct instruction *dest_insn, *orig_insn = insn;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001585
1586 /*
1587 * Backward search using the @first_jump_src links, these help avoid
1588 * much of the 'in between' code. Which avoids us getting confused by
1589 * it.
1590 */
1591 for (;
Olivier Deprez157378f2022-04-04 15:47:50 +02001592 insn && insn->func && insn->func->pfunc == func;
1593 insn = insn->first_jump_src ?: prev_insn_same_sym(file, insn)) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001594
1595 if (insn != orig_insn && insn->type == INSN_JUMP_DYNAMIC)
1596 break;
1597
1598 /* allow small jumps within the range */
1599 if (insn->type == INSN_JUMP_UNCONDITIONAL &&
1600 insn->jump_dest &&
1601 (insn->jump_dest->offset <= insn->offset ||
1602 insn->jump_dest->offset > orig_insn->offset))
1603 break;
1604
Olivier Deprez157378f2022-04-04 15:47:50 +02001605 table_reloc = arch_find_switch_table(file, insn);
1606 if (!table_reloc)
1607 continue;
1608 dest_insn = find_insn(file, table_reloc->sym->sec, table_reloc->addend);
1609 if (!dest_insn || !dest_insn->func || dest_insn->func->pfunc != func)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001610 continue;
1611
Olivier Deprez157378f2022-04-04 15:47:50 +02001612 return table_reloc;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001613 }
1614
1615 return NULL;
1616}
1617
David Brazdil0f672f62019-12-10 10:32:29 +00001618/*
1619 * First pass: Mark the head of each jump table so that in the next pass,
1620 * we know when a given jump table ends and the next one starts.
1621 */
1622static void mark_func_jump_tables(struct objtool_file *file,
1623 struct symbol *func)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001624{
David Brazdil0f672f62019-12-10 10:32:29 +00001625 struct instruction *insn, *last = NULL;
Olivier Deprez157378f2022-04-04 15:47:50 +02001626 struct reloc *reloc;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001627
Olivier Deprez157378f2022-04-04 15:47:50 +02001628 func_for_each_insn(file, func, insn) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001629 if (!last)
1630 last = insn;
1631
1632 /*
1633 * Store back-pointers for unconditional forward jumps such
David Brazdil0f672f62019-12-10 10:32:29 +00001634 * that find_jump_table() can back-track using those and
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001635 * avoid some potentially confusing code.
1636 */
1637 if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest &&
1638 insn->offset > last->offset &&
1639 insn->jump_dest->offset > insn->offset &&
1640 !insn->jump_dest->first_jump_src) {
1641
1642 insn->jump_dest->first_jump_src = insn;
1643 last = insn->jump_dest;
1644 }
1645
1646 if (insn->type != INSN_JUMP_DYNAMIC)
1647 continue;
1648
Olivier Deprez157378f2022-04-04 15:47:50 +02001649 reloc = find_jump_table(file, func, insn);
1650 if (reloc) {
1651 reloc->jump_table_start = true;
1652 insn->jump_table = reloc;
David Brazdil0f672f62019-12-10 10:32:29 +00001653 }
1654 }
1655}
1656
1657static int add_func_jump_tables(struct objtool_file *file,
1658 struct symbol *func)
1659{
1660 struct instruction *insn;
1661 int ret;
1662
Olivier Deprez157378f2022-04-04 15:47:50 +02001663 func_for_each_insn(file, func, insn) {
David Brazdil0f672f62019-12-10 10:32:29 +00001664 if (!insn->jump_table)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001665 continue;
1666
David Brazdil0f672f62019-12-10 10:32:29 +00001667 ret = add_jump_table(file, insn, insn->jump_table);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001668 if (ret)
1669 return ret;
1670 }
1671
1672 return 0;
1673}
1674
1675/*
1676 * For some switch statements, gcc generates a jump table in the .rodata
1677 * section which contains a list of addresses within the function to jump to.
1678 * This finds these jump tables and adds them to the insn->alts lists.
1679 */
David Brazdil0f672f62019-12-10 10:32:29 +00001680static int add_jump_table_alts(struct objtool_file *file)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001681{
1682 struct section *sec;
1683 struct symbol *func;
1684 int ret;
1685
David Brazdil0f672f62019-12-10 10:32:29 +00001686 if (!file->rodata)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001687 return 0;
1688
1689 for_each_sec(file, sec) {
1690 list_for_each_entry(func, &sec->symbol_list, list) {
1691 if (func->type != STT_FUNC)
1692 continue;
1693
David Brazdil0f672f62019-12-10 10:32:29 +00001694 mark_func_jump_tables(file, func);
1695 ret = add_func_jump_tables(file, func);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001696 if (ret)
1697 return ret;
1698 }
1699 }
1700
1701 return 0;
1702}
1703
Olivier Deprez92d4c212022-12-06 15:05:30 +01001704static void set_func_state(struct cfi_state *state)
1705{
1706 state->cfa = initial_func_cfi.cfa;
1707 memcpy(&state->regs, &initial_func_cfi.regs,
1708 CFI_NUM_REGS * sizeof(struct cfi_reg));
1709 state->stack_size = initial_func_cfi.cfa.offset;
1710}
1711
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001712static int read_unwind_hints(struct objtool_file *file)
1713{
Olivier Deprez92d4c212022-12-06 15:05:30 +01001714 struct cfi_state cfi = init_cfi;
Olivier Deprez157378f2022-04-04 15:47:50 +02001715 struct section *sec, *relocsec;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001716 struct unwind_hint *hint;
1717 struct instruction *insn;
Olivier Deprez92d4c212022-12-06 15:05:30 +01001718 struct reloc *reloc;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001719 int i;
1720
1721 sec = find_section_by_name(file->elf, ".discard.unwind_hints");
1722 if (!sec)
1723 return 0;
1724
Olivier Deprez157378f2022-04-04 15:47:50 +02001725 relocsec = sec->reloc;
1726 if (!relocsec) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001727 WARN("missing .rela.discard.unwind_hints section");
1728 return -1;
1729 }
1730
1731 if (sec->len % sizeof(struct unwind_hint)) {
1732 WARN("struct unwind_hint size mismatch");
1733 return -1;
1734 }
1735
1736 file->hints = true;
1737
1738 for (i = 0; i < sec->len / sizeof(struct unwind_hint); i++) {
1739 hint = (struct unwind_hint *)sec->data->d_buf + i;
1740
Olivier Deprez157378f2022-04-04 15:47:50 +02001741 reloc = find_reloc_by_dest(file->elf, sec, i * sizeof(*hint));
1742 if (!reloc) {
1743 WARN("can't find reloc for unwind_hints[%d]", i);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001744 return -1;
1745 }
1746
Olivier Deprez157378f2022-04-04 15:47:50 +02001747 insn = find_insn(file, reloc->sym->sec, reloc->addend);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001748 if (!insn) {
1749 WARN("can't find insn for unwind_hints[%d]", i);
1750 return -1;
1751 }
1752
Olivier Deprez92d4c212022-12-06 15:05:30 +01001753 insn->hint = true;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001754
Olivier Deprez92d4c212022-12-06 15:05:30 +01001755 if (hint->type == UNWIND_HINT_TYPE_SAVE) {
1756 insn->hint = false;
1757 insn->save = true;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001758 continue;
1759 }
1760
Olivier Deprez92d4c212022-12-06 15:05:30 +01001761 if (hint->type == UNWIND_HINT_TYPE_RESTORE) {
1762 insn->restore = true;
1763 continue;
1764 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001765
Olivier Deprez92d4c212022-12-06 15:05:30 +01001766 if (hint->type == UNWIND_HINT_TYPE_REGS_PARTIAL) {
1767 struct symbol *sym = find_symbol_by_offset(insn->sec, insn->offset);
1768
1769 if (sym && sym->bind == STB_GLOBAL) {
1770 insn->entry = 1;
1771 }
1772 }
1773
1774 if (hint->type == UNWIND_HINT_TYPE_ENTRY) {
1775 hint->type = UNWIND_HINT_TYPE_CALL;
1776 insn->entry = 1;
1777 }
1778
1779 if (hint->type == UNWIND_HINT_TYPE_FUNC) {
1780 insn->cfi = &func_cfi;
1781 continue;
1782 }
1783
1784 if (insn->cfi)
1785 cfi = *(insn->cfi);
1786
1787 if (arch_decode_hint_reg(hint->sp_reg, &cfi.cfa.base)) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001788 WARN_FUNC("unsupported unwind_hint sp base reg %d",
1789 insn->sec, insn->offset, hint->sp_reg);
1790 return -1;
1791 }
1792
Olivier Deprez92d4c212022-12-06 15:05:30 +01001793 cfi.cfa.offset = hint->sp_offset;
1794 cfi.type = hint->type;
1795 cfi.end = hint->end;
1796
1797 insn->cfi = cfi_hash_find_or_add(&cfi);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001798 }
1799
1800 return 0;
1801}
1802
1803static int read_retpoline_hints(struct objtool_file *file)
1804{
1805 struct section *sec;
1806 struct instruction *insn;
Olivier Deprez157378f2022-04-04 15:47:50 +02001807 struct reloc *reloc;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001808
1809 sec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe");
1810 if (!sec)
1811 return 0;
1812
Olivier Deprez157378f2022-04-04 15:47:50 +02001813 list_for_each_entry(reloc, &sec->reloc_list, list) {
1814 if (reloc->sym->type != STT_SECTION) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001815 WARN("unexpected relocation symbol type in %s", sec->name);
1816 return -1;
1817 }
1818
Olivier Deprez157378f2022-04-04 15:47:50 +02001819 insn = find_insn(file, reloc->sym->sec, reloc->addend);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001820 if (!insn) {
1821 WARN("bad .discard.retpoline_safe entry");
1822 return -1;
1823 }
1824
1825 if (insn->type != INSN_JUMP_DYNAMIC &&
Olivier Deprez92d4c212022-12-06 15:05:30 +01001826 insn->type != INSN_CALL_DYNAMIC &&
1827 insn->type != INSN_RETURN &&
1828 insn->type != INSN_NOP) {
1829 WARN_FUNC("retpoline_safe hint not an indirect jump/call/ret/nop",
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001830 insn->sec, insn->offset);
1831 return -1;
1832 }
1833
1834 insn->retpoline_safe = true;
1835 }
1836
1837 return 0;
1838}
1839
Olivier Deprez157378f2022-04-04 15:47:50 +02001840static int read_instr_hints(struct objtool_file *file)
1841{
1842 struct section *sec;
1843 struct instruction *insn;
1844 struct reloc *reloc;
1845
1846 sec = find_section_by_name(file->elf, ".rela.discard.instr_end");
1847 if (!sec)
1848 return 0;
1849
1850 list_for_each_entry(reloc, &sec->reloc_list, list) {
1851 if (reloc->sym->type != STT_SECTION) {
1852 WARN("unexpected relocation symbol type in %s", sec->name);
1853 return -1;
1854 }
1855
1856 insn = find_insn(file, reloc->sym->sec, reloc->addend);
1857 if (!insn) {
1858 WARN("bad .discard.instr_end entry");
1859 return -1;
1860 }
1861
1862 insn->instr--;
1863 }
1864
1865 sec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
1866 if (!sec)
1867 return 0;
1868
1869 list_for_each_entry(reloc, &sec->reloc_list, list) {
1870 if (reloc->sym->type != STT_SECTION) {
1871 WARN("unexpected relocation symbol type in %s", sec->name);
1872 return -1;
1873 }
1874
1875 insn = find_insn(file, reloc->sym->sec, reloc->addend);
1876 if (!insn) {
1877 WARN("bad .discard.instr_begin entry");
1878 return -1;
1879 }
1880
1881 insn->instr++;
1882 }
1883
1884 return 0;
1885}
1886
1887static int read_intra_function_calls(struct objtool_file *file)
1888{
1889 struct instruction *insn;
1890 struct section *sec;
1891 struct reloc *reloc;
1892
1893 sec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls");
1894 if (!sec)
1895 return 0;
1896
1897 list_for_each_entry(reloc, &sec->reloc_list, list) {
1898 unsigned long dest_off;
1899
1900 if (reloc->sym->type != STT_SECTION) {
1901 WARN("unexpected relocation symbol type in %s",
1902 sec->name);
1903 return -1;
1904 }
1905
1906 insn = find_insn(file, reloc->sym->sec, reloc->addend);
1907 if (!insn) {
1908 WARN("bad .discard.intra_function_call entry");
1909 return -1;
1910 }
1911
1912 if (insn->type != INSN_CALL) {
1913 WARN_FUNC("intra_function_call not a direct call",
1914 insn->sec, insn->offset);
1915 return -1;
1916 }
1917
1918 /*
1919 * Treat intra-function CALLs as JMPs, but with a stack_op.
1920 * See add_call_destinations(), which strips stack_ops from
1921 * normal CALLs.
1922 */
1923 insn->type = INSN_JUMP_UNCONDITIONAL;
1924
1925 dest_off = insn->offset + insn->len + insn->immediate;
1926 insn->jump_dest = find_insn(file, insn->sec, dest_off);
1927 if (!insn->jump_dest) {
1928 WARN_FUNC("can't find call dest at %s+0x%lx",
1929 insn->sec, insn->offset,
1930 insn->sec->name, dest_off);
1931 return -1;
1932 }
1933 }
1934
1935 return 0;
1936}
1937
Olivier Deprez92d4c212022-12-06 15:05:30 +01001938static int classify_symbols(struct objtool_file *file)
Olivier Deprez157378f2022-04-04 15:47:50 +02001939{
1940 struct section *sec;
1941 struct symbol *func;
1942
1943 for_each_sec(file, sec) {
1944 list_for_each_entry(func, &sec->symbol_list, list) {
Olivier Deprez92d4c212022-12-06 15:05:30 +01001945 if (func->bind != STB_GLOBAL)
1946 continue;
1947
1948 if (!strncmp(func->name, STATIC_CALL_TRAMP_PREFIX_STR,
Olivier Deprez157378f2022-04-04 15:47:50 +02001949 strlen(STATIC_CALL_TRAMP_PREFIX_STR)))
1950 func->static_call_tramp = true;
Olivier Deprez92d4c212022-12-06 15:05:30 +01001951
1952 if (arch_is_retpoline(func))
1953 func->retpoline_thunk = true;
1954
1955 if (arch_is_rethunk(func))
1956 func->return_thunk = true;
1957
1958 if (!strcmp(func->name, "__fentry__"))
1959 func->fentry = true;
1960
1961 if (!strncmp(func->name, "__sanitizer_cov_", 16))
1962 func->kcov = true;
Olivier Deprez157378f2022-04-04 15:47:50 +02001963 }
1964 }
1965
1966 return 0;
1967}
1968
David Brazdil0f672f62019-12-10 10:32:29 +00001969static void mark_rodata(struct objtool_file *file)
1970{
1971 struct section *sec;
1972 bool found = false;
1973
1974 /*
1975 * Search for the following rodata sections, each of which can
1976 * potentially contain jump tables:
1977 *
1978 * - .rodata: can contain GCC switch tables
1979 * - .rodata.<func>: same, if -fdata-sections is being used
1980 * - .rodata..c_jump_table: contains C annotated jump tables
1981 *
1982 * .rodata.str1.* sections are ignored; they don't contain jump tables.
1983 */
1984 for_each_sec(file, sec) {
Olivier Deprez157378f2022-04-04 15:47:50 +02001985 if (!strncmp(sec->name, ".rodata", 7) &&
1986 !strstr(sec->name, ".str1.")) {
David Brazdil0f672f62019-12-10 10:32:29 +00001987 sec->rodata = true;
1988 found = true;
1989 }
1990 }
1991
1992 file->rodata = found;
1993}
1994
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001995static int decode_sections(struct objtool_file *file)
1996{
1997 int ret;
1998
David Brazdil0f672f62019-12-10 10:32:29 +00001999 mark_rodata(file);
2000
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002001 ret = decode_instructions(file);
2002 if (ret)
2003 return ret;
2004
2005 ret = add_dead_ends(file);
2006 if (ret)
2007 return ret;
2008
2009 add_ignores(file);
David Brazdil0f672f62019-12-10 10:32:29 +00002010 add_uaccess_safe(file);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002011
David Brazdil0f672f62019-12-10 10:32:29 +00002012 ret = add_ignore_alternatives(file);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002013 if (ret)
2014 return ret;
2015
Olivier Deprez157378f2022-04-04 15:47:50 +02002016 /*
2017 * Must be before add_{jump_call}_destination.
2018 */
Olivier Deprez92d4c212022-12-06 15:05:30 +01002019 ret = classify_symbols(file);
Olivier Deprez157378f2022-04-04 15:47:50 +02002020 if (ret)
2021 return ret;
2022
Olivier Deprez92d4c212022-12-06 15:05:30 +01002023 /*
2024 * Must be before add_special_section_alts() as that depends on
2025 * jump_dest being set.
2026 */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002027 ret = add_jump_destinations(file);
2028 if (ret)
2029 return ret;
2030
2031 ret = add_special_section_alts(file);
2032 if (ret)
2033 return ret;
2034
Olivier Deprez157378f2022-04-04 15:47:50 +02002035 /*
2036 * Must be before add_call_destination(); it changes INSN_CALL to
2037 * INSN_JUMP.
2038 */
2039 ret = read_intra_function_calls(file);
2040 if (ret)
2041 return ret;
2042
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002043 ret = add_call_destinations(file);
2044 if (ret)
2045 return ret;
2046
David Brazdil0f672f62019-12-10 10:32:29 +00002047 ret = add_jump_table_alts(file);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002048 if (ret)
2049 return ret;
2050
2051 ret = read_unwind_hints(file);
2052 if (ret)
2053 return ret;
2054
2055 ret = read_retpoline_hints(file);
2056 if (ret)
2057 return ret;
2058
Olivier Deprez157378f2022-04-04 15:47:50 +02002059 ret = read_instr_hints(file);
2060 if (ret)
2061 return ret;
2062
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002063 return 0;
2064}
2065
2066static bool is_fentry_call(struct instruction *insn)
2067{
Olivier Deprez92d4c212022-12-06 15:05:30 +01002068 if (insn->type == INSN_CALL &&
2069 insn->call_dest &&
2070 insn->call_dest->fentry)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002071 return true;
2072
2073 return false;
2074}
2075
Olivier Deprez157378f2022-04-04 15:47:50 +02002076static bool has_modified_stack_frame(struct instruction *insn, struct insn_state *state)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002077{
Olivier Deprez157378f2022-04-04 15:47:50 +02002078 struct cfi_state *cfi = &state->cfi;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002079 int i;
2080
Olivier Deprez157378f2022-04-04 15:47:50 +02002081 if (cfi->cfa.base != initial_func_cfi.cfa.base || cfi->drap)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002082 return true;
2083
Olivier Deprez92d4c212022-12-06 15:05:30 +01002084 if (cfi->cfa.offset != initial_func_cfi.cfa.offset)
Olivier Deprez157378f2022-04-04 15:47:50 +02002085 return true;
2086
Olivier Deprez92d4c212022-12-06 15:05:30 +01002087 if (cfi->stack_size != initial_func_cfi.cfa.offset)
Olivier Deprez157378f2022-04-04 15:47:50 +02002088 return true;
2089
Olivier Deprez157378f2022-04-04 15:47:50 +02002090 for (i = 0; i < CFI_NUM_REGS; i++) {
2091 if (cfi->regs[i].base != initial_func_cfi.regs[i].base ||
2092 cfi->regs[i].offset != initial_func_cfi.regs[i].offset)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002093 return true;
Olivier Deprez157378f2022-04-04 15:47:50 +02002094 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002095
2096 return false;
2097}
2098
2099static bool has_valid_stack_frame(struct insn_state *state)
2100{
Olivier Deprez157378f2022-04-04 15:47:50 +02002101 struct cfi_state *cfi = &state->cfi;
2102
2103 if (cfi->cfa.base == CFI_BP && cfi->regs[CFI_BP].base == CFI_CFA &&
2104 cfi->regs[CFI_BP].offset == -16)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002105 return true;
2106
Olivier Deprez157378f2022-04-04 15:47:50 +02002107 if (cfi->drap && cfi->regs[CFI_BP].base == CFI_BP)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002108 return true;
2109
2110 return false;
2111}
2112
Olivier Deprez157378f2022-04-04 15:47:50 +02002113static int update_cfi_state_regs(struct instruction *insn,
2114 struct cfi_state *cfi,
2115 struct stack_op *op)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002116{
Olivier Deprez157378f2022-04-04 15:47:50 +02002117 struct cfi_reg *cfa = &cfi->cfa;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002118
Olivier Deprez0e641232021-09-23 10:07:05 +02002119 if (cfa->base != CFI_SP && cfa->base != CFI_SP_INDIRECT)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002120 return 0;
2121
2122 /* push */
David Brazdil0f672f62019-12-10 10:32:29 +00002123 if (op->dest.type == OP_DEST_PUSH || op->dest.type == OP_DEST_PUSHF)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002124 cfa->offset += 8;
2125
2126 /* pop */
David Brazdil0f672f62019-12-10 10:32:29 +00002127 if (op->src.type == OP_SRC_POP || op->src.type == OP_SRC_POPF)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002128 cfa->offset -= 8;
2129
2130 /* add immediate to sp */
2131 if (op->dest.type == OP_DEST_REG && op->src.type == OP_SRC_ADD &&
2132 op->dest.reg == CFI_SP && op->src.reg == CFI_SP)
2133 cfa->offset -= op->src.offset;
2134
2135 return 0;
2136}
2137
Olivier Deprez157378f2022-04-04 15:47:50 +02002138static void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int offset)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002139{
2140 if (arch_callee_saved_reg(reg) &&
Olivier Deprez157378f2022-04-04 15:47:50 +02002141 cfi->regs[reg].base == CFI_UNDEFINED) {
2142 cfi->regs[reg].base = base;
2143 cfi->regs[reg].offset = offset;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002144 }
2145}
2146
Olivier Deprez157378f2022-04-04 15:47:50 +02002147static void restore_reg(struct cfi_state *cfi, unsigned char reg)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002148{
Olivier Deprez157378f2022-04-04 15:47:50 +02002149 cfi->regs[reg].base = initial_func_cfi.regs[reg].base;
2150 cfi->regs[reg].offset = initial_func_cfi.regs[reg].offset;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002151}
2152
2153/*
2154 * A note about DRAP stack alignment:
2155 *
2156 * GCC has the concept of a DRAP register, which is used to help keep track of
2157 * the stack pointer when aligning the stack. r10 or r13 is used as the DRAP
2158 * register. The typical DRAP pattern is:
2159 *
2160 * 4c 8d 54 24 08 lea 0x8(%rsp),%r10
2161 * 48 83 e4 c0 and $0xffffffffffffffc0,%rsp
2162 * 41 ff 72 f8 pushq -0x8(%r10)
2163 * 55 push %rbp
2164 * 48 89 e5 mov %rsp,%rbp
2165 * (more pushes)
2166 * 41 52 push %r10
2167 * ...
2168 * 41 5a pop %r10
2169 * (more pops)
2170 * 5d pop %rbp
2171 * 49 8d 62 f8 lea -0x8(%r10),%rsp
2172 * c3 retq
2173 *
2174 * There are some variations in the epilogues, like:
2175 *
2176 * 5b pop %rbx
2177 * 41 5a pop %r10
2178 * 41 5c pop %r12
2179 * 41 5d pop %r13
2180 * 41 5e pop %r14
2181 * c9 leaveq
2182 * 49 8d 62 f8 lea -0x8(%r10),%rsp
2183 * c3 retq
2184 *
2185 * and:
2186 *
2187 * 4c 8b 55 e8 mov -0x18(%rbp),%r10
2188 * 48 8b 5d e0 mov -0x20(%rbp),%rbx
2189 * 4c 8b 65 f0 mov -0x10(%rbp),%r12
2190 * 4c 8b 6d f8 mov -0x8(%rbp),%r13
2191 * c9 leaveq
2192 * 49 8d 62 f8 lea -0x8(%r10),%rsp
2193 * c3 retq
2194 *
2195 * Sometimes r13 is used as the DRAP register, in which case it's saved and
2196 * restored beforehand:
2197 *
2198 * 41 55 push %r13
2199 * 4c 8d 6c 24 10 lea 0x10(%rsp),%r13
2200 * 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
2201 * ...
2202 * 49 8d 65 f0 lea -0x10(%r13),%rsp
2203 * 41 5d pop %r13
2204 * c3 retq
2205 */
Olivier Deprez157378f2022-04-04 15:47:50 +02002206static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
2207 struct stack_op *op)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002208{
Olivier Deprez157378f2022-04-04 15:47:50 +02002209 struct cfi_reg *cfa = &cfi->cfa;
2210 struct cfi_reg *regs = cfi->regs;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002211
2212 /* stack operations don't make sense with an undefined CFA */
2213 if (cfa->base == CFI_UNDEFINED) {
2214 if (insn->func) {
2215 WARN_FUNC("undefined stack state", insn->sec, insn->offset);
2216 return -1;
2217 }
2218 return 0;
2219 }
2220
Olivier Deprez157378f2022-04-04 15:47:50 +02002221 if (cfi->type == UNWIND_HINT_TYPE_REGS ||
2222 cfi->type == UNWIND_HINT_TYPE_REGS_PARTIAL)
2223 return update_cfi_state_regs(insn, cfi, op);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002224
2225 switch (op->dest.type) {
2226
2227 case OP_DEST_REG:
2228 switch (op->src.type) {
2229
2230 case OP_SRC_REG:
2231 if (op->src.reg == CFI_SP && op->dest.reg == CFI_BP &&
2232 cfa->base == CFI_SP &&
2233 regs[CFI_BP].base == CFI_CFA &&
2234 regs[CFI_BP].offset == -cfa->offset) {
2235
2236 /* mov %rsp, %rbp */
2237 cfa->base = op->dest.reg;
Olivier Deprez157378f2022-04-04 15:47:50 +02002238 cfi->bp_scratch = false;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002239 }
2240
2241 else if (op->src.reg == CFI_SP &&
Olivier Deprez157378f2022-04-04 15:47:50 +02002242 op->dest.reg == CFI_BP && cfi->drap) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002243
2244 /* drap: mov %rsp, %rbp */
2245 regs[CFI_BP].base = CFI_BP;
Olivier Deprez157378f2022-04-04 15:47:50 +02002246 regs[CFI_BP].offset = -cfi->stack_size;
2247 cfi->bp_scratch = false;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002248 }
2249
2250 else if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
2251
2252 /*
2253 * mov %rsp, %reg
2254 *
2255 * This is needed for the rare case where GCC
2256 * does:
2257 *
2258 * mov %rsp, %rax
2259 * ...
2260 * mov %rax, %rsp
2261 */
Olivier Deprez157378f2022-04-04 15:47:50 +02002262 cfi->vals[op->dest.reg].base = CFI_CFA;
2263 cfi->vals[op->dest.reg].offset = -cfi->stack_size;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002264 }
2265
2266 else if (op->src.reg == CFI_BP && op->dest.reg == CFI_SP &&
2267 cfa->base == CFI_BP) {
2268
2269 /*
2270 * mov %rbp, %rsp
2271 *
2272 * Restore the original stack pointer (Clang).
2273 */
Olivier Deprez157378f2022-04-04 15:47:50 +02002274 cfi->stack_size = -cfi->regs[CFI_BP].offset;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002275 }
2276
2277 else if (op->dest.reg == cfa->base) {
2278
2279 /* mov %reg, %rsp */
2280 if (cfa->base == CFI_SP &&
Olivier Deprez157378f2022-04-04 15:47:50 +02002281 cfi->vals[op->src.reg].base == CFI_CFA) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002282
2283 /*
2284 * This is needed for the rare case
2285 * where GCC does something dumb like:
2286 *
2287 * lea 0x8(%rsp), %rcx
2288 * ...
2289 * mov %rcx, %rsp
2290 */
Olivier Deprez157378f2022-04-04 15:47:50 +02002291 cfa->offset = -cfi->vals[op->src.reg].offset;
2292 cfi->stack_size = cfa->offset;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002293
2294 } else {
2295 cfa->base = CFI_UNDEFINED;
2296 cfa->offset = 0;
2297 }
2298 }
2299
2300 break;
2301
2302 case OP_SRC_ADD:
2303 if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) {
2304
2305 /* add imm, %rsp */
Olivier Deprez157378f2022-04-04 15:47:50 +02002306 cfi->stack_size -= op->src.offset;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002307 if (cfa->base == CFI_SP)
2308 cfa->offset -= op->src.offset;
2309 break;
2310 }
2311
2312 if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) {
2313
2314 /* lea disp(%rbp), %rsp */
Olivier Deprez157378f2022-04-04 15:47:50 +02002315 cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002316 break;
2317 }
2318
2319 if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
2320
2321 /* drap: lea disp(%rsp), %drap */
Olivier Deprez157378f2022-04-04 15:47:50 +02002322 cfi->drap_reg = op->dest.reg;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002323
2324 /*
2325 * lea disp(%rsp), %reg
2326 *
2327 * This is needed for the rare case where GCC
2328 * does something dumb like:
2329 *
2330 * lea 0x8(%rsp), %rcx
2331 * ...
2332 * mov %rcx, %rsp
2333 */
Olivier Deprez157378f2022-04-04 15:47:50 +02002334 cfi->vals[op->dest.reg].base = CFI_CFA;
2335 cfi->vals[op->dest.reg].offset = \
2336 -cfi->stack_size + op->src.offset;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002337
2338 break;
2339 }
2340
Olivier Deprez157378f2022-04-04 15:47:50 +02002341 if (cfi->drap && op->dest.reg == CFI_SP &&
2342 op->src.reg == cfi->drap_reg) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002343
2344 /* drap: lea disp(%drap), %rsp */
2345 cfa->base = CFI_SP;
Olivier Deprez157378f2022-04-04 15:47:50 +02002346 cfa->offset = cfi->stack_size = -op->src.offset;
2347 cfi->drap_reg = CFI_UNDEFINED;
2348 cfi->drap = false;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002349 break;
2350 }
2351
Olivier Deprez157378f2022-04-04 15:47:50 +02002352 if (op->dest.reg == cfi->cfa.base) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002353 WARN_FUNC("unsupported stack register modification",
2354 insn->sec, insn->offset);
2355 return -1;
2356 }
2357
2358 break;
2359
2360 case OP_SRC_AND:
2361 if (op->dest.reg != CFI_SP ||
Olivier Deprez157378f2022-04-04 15:47:50 +02002362 (cfi->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
2363 (cfi->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002364 WARN_FUNC("unsupported stack pointer realignment",
2365 insn->sec, insn->offset);
2366 return -1;
2367 }
2368
Olivier Deprez157378f2022-04-04 15:47:50 +02002369 if (cfi->drap_reg != CFI_UNDEFINED) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002370 /* drap: and imm, %rsp */
Olivier Deprez157378f2022-04-04 15:47:50 +02002371 cfa->base = cfi->drap_reg;
2372 cfa->offset = cfi->stack_size = 0;
2373 cfi->drap = true;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002374 }
2375
2376 /*
2377 * Older versions of GCC (4.8ish) realign the stack
2378 * without DRAP, with a frame pointer.
2379 */
2380
2381 break;
2382
2383 case OP_SRC_POP:
David Brazdil0f672f62019-12-10 10:32:29 +00002384 case OP_SRC_POPF:
Olivier Deprez157378f2022-04-04 15:47:50 +02002385 if (!cfi->drap && op->dest.reg == cfa->base) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002386
2387 /* pop %rbp */
2388 cfa->base = CFI_SP;
2389 }
2390
Olivier Deprez157378f2022-04-04 15:47:50 +02002391 if (cfi->drap && cfa->base == CFI_BP_INDIRECT &&
2392 op->dest.reg == cfi->drap_reg &&
2393 cfi->drap_offset == -cfi->stack_size) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002394
2395 /* drap: pop %drap */
Olivier Deprez157378f2022-04-04 15:47:50 +02002396 cfa->base = cfi->drap_reg;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002397 cfa->offset = 0;
Olivier Deprez157378f2022-04-04 15:47:50 +02002398 cfi->drap_offset = -1;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002399
Olivier Deprez157378f2022-04-04 15:47:50 +02002400 } else if (regs[op->dest.reg].offset == -cfi->stack_size) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002401
2402 /* pop %reg */
Olivier Deprez157378f2022-04-04 15:47:50 +02002403 restore_reg(cfi, op->dest.reg);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002404 }
2405
Olivier Deprez157378f2022-04-04 15:47:50 +02002406 cfi->stack_size -= 8;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002407 if (cfa->base == CFI_SP)
2408 cfa->offset -= 8;
2409
2410 break;
2411
2412 case OP_SRC_REG_INDIRECT:
Olivier Deprez157378f2022-04-04 15:47:50 +02002413 if (cfi->drap && op->src.reg == CFI_BP &&
2414 op->src.offset == cfi->drap_offset) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002415
2416 /* drap: mov disp(%rbp), %drap */
Olivier Deprez157378f2022-04-04 15:47:50 +02002417 cfa->base = cfi->drap_reg;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002418 cfa->offset = 0;
Olivier Deprez157378f2022-04-04 15:47:50 +02002419 cfi->drap_offset = -1;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002420 }
2421
Olivier Deprez157378f2022-04-04 15:47:50 +02002422 if (cfi->drap && op->src.reg == CFI_BP &&
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002423 op->src.offset == regs[op->dest.reg].offset) {
2424
2425 /* drap: mov disp(%rbp), %reg */
Olivier Deprez157378f2022-04-04 15:47:50 +02002426 restore_reg(cfi, op->dest.reg);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002427
2428 } else if (op->src.reg == cfa->base &&
2429 op->src.offset == regs[op->dest.reg].offset + cfa->offset) {
2430
2431 /* mov disp(%rbp), %reg */
2432 /* mov disp(%rsp), %reg */
Olivier Deprez157378f2022-04-04 15:47:50 +02002433 restore_reg(cfi, op->dest.reg);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002434 }
2435
2436 break;
2437
2438 default:
2439 WARN_FUNC("unknown stack-related instruction",
2440 insn->sec, insn->offset);
2441 return -1;
2442 }
2443
2444 break;
2445
2446 case OP_DEST_PUSH:
David Brazdil0f672f62019-12-10 10:32:29 +00002447 case OP_DEST_PUSHF:
Olivier Deprez157378f2022-04-04 15:47:50 +02002448 cfi->stack_size += 8;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002449 if (cfa->base == CFI_SP)
2450 cfa->offset += 8;
2451
2452 if (op->src.type != OP_SRC_REG)
2453 break;
2454
Olivier Deprez157378f2022-04-04 15:47:50 +02002455 if (cfi->drap) {
2456 if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002457
2458 /* drap: push %drap */
2459 cfa->base = CFI_BP_INDIRECT;
Olivier Deprez157378f2022-04-04 15:47:50 +02002460 cfa->offset = -cfi->stack_size;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002461
2462 /* save drap so we know when to restore it */
Olivier Deprez157378f2022-04-04 15:47:50 +02002463 cfi->drap_offset = -cfi->stack_size;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002464
Olivier Deprez157378f2022-04-04 15:47:50 +02002465 } else if (op->src.reg == CFI_BP && cfa->base == cfi->drap_reg) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002466
2467 /* drap: push %rbp */
Olivier Deprez157378f2022-04-04 15:47:50 +02002468 cfi->stack_size = 0;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002469
Olivier Deprez157378f2022-04-04 15:47:50 +02002470 } else {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002471
2472 /* drap: push %reg */
Olivier Deprez157378f2022-04-04 15:47:50 +02002473 save_reg(cfi, op->src.reg, CFI_BP, -cfi->stack_size);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002474 }
2475
2476 } else {
2477
2478 /* push %reg */
Olivier Deprez157378f2022-04-04 15:47:50 +02002479 save_reg(cfi, op->src.reg, CFI_CFA, -cfi->stack_size);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002480 }
2481
2482 /* detect when asm code uses rbp as a scratch register */
2483 if (!no_fp && insn->func && op->src.reg == CFI_BP &&
2484 cfa->base != CFI_BP)
Olivier Deprez157378f2022-04-04 15:47:50 +02002485 cfi->bp_scratch = true;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002486 break;
2487
2488 case OP_DEST_REG_INDIRECT:
2489
Olivier Deprez157378f2022-04-04 15:47:50 +02002490 if (cfi->drap) {
2491 if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002492
2493 /* drap: mov %drap, disp(%rbp) */
2494 cfa->base = CFI_BP_INDIRECT;
2495 cfa->offset = op->dest.offset;
2496
2497 /* save drap offset so we know when to restore it */
Olivier Deprez157378f2022-04-04 15:47:50 +02002498 cfi->drap_offset = op->dest.offset;
2499 } else {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002500
2501 /* drap: mov reg, disp(%rbp) */
Olivier Deprez157378f2022-04-04 15:47:50 +02002502 save_reg(cfi, op->src.reg, CFI_BP, op->dest.offset);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002503 }
2504
2505 } else if (op->dest.reg == cfa->base) {
2506
2507 /* mov reg, disp(%rbp) */
2508 /* mov reg, disp(%rsp) */
Olivier Deprez157378f2022-04-04 15:47:50 +02002509 save_reg(cfi, op->src.reg, CFI_CFA,
2510 op->dest.offset - cfi->cfa.offset);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002511 }
2512
2513 break;
2514
2515 case OP_DEST_LEAVE:
Olivier Deprez157378f2022-04-04 15:47:50 +02002516 if ((!cfi->drap && cfa->base != CFI_BP) ||
2517 (cfi->drap && cfa->base != cfi->drap_reg)) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002518 WARN_FUNC("leave instruction with modified stack frame",
2519 insn->sec, insn->offset);
2520 return -1;
2521 }
2522
2523 /* leave (mov %rbp, %rsp; pop %rbp) */
2524
Olivier Deprez157378f2022-04-04 15:47:50 +02002525 cfi->stack_size = -cfi->regs[CFI_BP].offset - 8;
2526 restore_reg(cfi, CFI_BP);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002527
Olivier Deprez157378f2022-04-04 15:47:50 +02002528 if (!cfi->drap) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002529 cfa->base = CFI_SP;
2530 cfa->offset -= 8;
2531 }
2532
2533 break;
2534
2535 case OP_DEST_MEM:
David Brazdil0f672f62019-12-10 10:32:29 +00002536 if (op->src.type != OP_SRC_POP && op->src.type != OP_SRC_POPF) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002537 WARN_FUNC("unknown stack-related memory operation",
2538 insn->sec, insn->offset);
2539 return -1;
2540 }
2541
2542 /* pop mem */
Olivier Deprez157378f2022-04-04 15:47:50 +02002543 cfi->stack_size -= 8;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002544 if (cfa->base == CFI_SP)
2545 cfa->offset -= 8;
2546
2547 break;
2548
2549 default:
2550 WARN_FUNC("unknown stack-related instruction",
2551 insn->sec, insn->offset);
2552 return -1;
2553 }
2554
2555 return 0;
2556}
2557
Olivier Deprez92d4c212022-12-06 15:05:30 +01002558/*
2559 * The stack layouts of alternatives instructions can sometimes diverge when
2560 * they have stack modifications. That's fine as long as the potential stack
2561 * layouts don't conflict at any given potential instruction boundary.
2562 *
2563 * Flatten the CFIs of the different alternative code streams (both original
2564 * and replacement) into a single shared CFI array which can be used to detect
2565 * conflicts and nicely feed a linear array of ORC entries to the unwinder.
2566 */
2567static int propagate_alt_cfi(struct objtool_file *file, struct instruction *insn)
2568{
2569 struct cfi_state **alt_cfi;
2570 int group_off;
2571
2572 if (!insn->alt_group)
2573 return 0;
2574
2575 if (!insn->cfi) {
2576 WARN("CFI missing");
2577 return -1;
2578 }
2579
2580 alt_cfi = insn->alt_group->cfi;
2581 group_off = insn->offset - insn->alt_group->first_insn->offset;
2582
2583 if (!alt_cfi[group_off]) {
2584 alt_cfi[group_off] = insn->cfi;
2585 } else {
2586 if (cficmp(alt_cfi[group_off], insn->cfi)) {
2587 WARN_FUNC("stack layout conflict in alternatives",
2588 insn->sec, insn->offset);
2589 return -1;
2590 }
2591 }
2592
2593 return 0;
2594}
2595
Olivier Deprez157378f2022-04-04 15:47:50 +02002596static int handle_insn_ops(struct instruction *insn, struct insn_state *state)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002597{
Olivier Deprez157378f2022-04-04 15:47:50 +02002598 struct stack_op *op;
2599
2600 list_for_each_entry(op, &insn->stack_ops, list) {
Olivier Deprez157378f2022-04-04 15:47:50 +02002601
Olivier Deprez92d4c212022-12-06 15:05:30 +01002602 if (update_cfi_state(insn, &state->cfi, op))
2603 return 1;
Olivier Deprez157378f2022-04-04 15:47:50 +02002604
2605 if (op->dest.type == OP_DEST_PUSHF) {
2606 if (!state->uaccess_stack) {
2607 state->uaccess_stack = 1;
2608 } else if (state->uaccess_stack >> 31) {
2609 WARN_FUNC("PUSHF stack exhausted",
2610 insn->sec, insn->offset);
2611 return 1;
2612 }
2613 state->uaccess_stack <<= 1;
2614 state->uaccess_stack |= state->uaccess;
2615 }
2616
2617 if (op->src.type == OP_SRC_POPF) {
2618 if (state->uaccess_stack) {
2619 state->uaccess = state->uaccess_stack & 1;
2620 state->uaccess_stack >>= 1;
2621 if (state->uaccess_stack == 1)
2622 state->uaccess_stack = 0;
2623 }
2624 }
2625 }
2626
2627 return 0;
2628}
2629
2630static bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2)
2631{
Olivier Deprez92d4c212022-12-06 15:05:30 +01002632 struct cfi_state *cfi1 = insn->cfi;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002633 int i;
2634
Olivier Deprez92d4c212022-12-06 15:05:30 +01002635 if (!cfi1) {
2636 WARN("CFI missing");
2637 return false;
2638 }
2639
Olivier Deprez157378f2022-04-04 15:47:50 +02002640 if (memcmp(&cfi1->cfa, &cfi2->cfa, sizeof(cfi1->cfa))) {
2641
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002642 WARN_FUNC("stack state mismatch: cfa1=%d%+d cfa2=%d%+d",
2643 insn->sec, insn->offset,
Olivier Deprez157378f2022-04-04 15:47:50 +02002644 cfi1->cfa.base, cfi1->cfa.offset,
2645 cfi2->cfa.base, cfi2->cfa.offset);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002646
Olivier Deprez157378f2022-04-04 15:47:50 +02002647 } else if (memcmp(&cfi1->regs, &cfi2->regs, sizeof(cfi1->regs))) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002648 for (i = 0; i < CFI_NUM_REGS; i++) {
Olivier Deprez157378f2022-04-04 15:47:50 +02002649 if (!memcmp(&cfi1->regs[i], &cfi2->regs[i],
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002650 sizeof(struct cfi_reg)))
2651 continue;
2652
2653 WARN_FUNC("stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d",
2654 insn->sec, insn->offset,
Olivier Deprez157378f2022-04-04 15:47:50 +02002655 i, cfi1->regs[i].base, cfi1->regs[i].offset,
2656 i, cfi2->regs[i].base, cfi2->regs[i].offset);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002657 break;
2658 }
2659
Olivier Deprez157378f2022-04-04 15:47:50 +02002660 } else if (cfi1->type != cfi2->type) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002661
Olivier Deprez157378f2022-04-04 15:47:50 +02002662 WARN_FUNC("stack state mismatch: type1=%d type2=%d",
2663 insn->sec, insn->offset, cfi1->type, cfi2->type);
2664
2665 } else if (cfi1->drap != cfi2->drap ||
2666 (cfi1->drap && cfi1->drap_reg != cfi2->drap_reg) ||
2667 (cfi1->drap && cfi1->drap_offset != cfi2->drap_offset)) {
2668
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002669 WARN_FUNC("stack state mismatch: drap1=%d(%d,%d) drap2=%d(%d,%d)",
2670 insn->sec, insn->offset,
Olivier Deprez157378f2022-04-04 15:47:50 +02002671 cfi1->drap, cfi1->drap_reg, cfi1->drap_offset,
2672 cfi2->drap, cfi2->drap_reg, cfi2->drap_offset);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002673
2674 } else
2675 return true;
2676
2677 return false;
2678}
2679
David Brazdil0f672f62019-12-10 10:32:29 +00002680static inline bool func_uaccess_safe(struct symbol *func)
2681{
2682 if (func)
2683 return func->uaccess_safe;
2684
2685 return false;
2686}
2687
2688static inline const char *call_dest_name(struct instruction *insn)
2689{
2690 if (insn->call_dest)
2691 return insn->call_dest->name;
2692
2693 return "{dynamic}";
2694}
2695
Olivier Deprez157378f2022-04-04 15:47:50 +02002696static inline bool noinstr_call_dest(struct symbol *func)
2697{
2698 /*
2699 * We can't deal with indirect function calls at present;
2700 * assume they're instrumented.
2701 */
2702 if (!func)
2703 return false;
2704
2705 /*
2706 * If the symbol is from a noinstr section; we good.
2707 */
2708 if (func->sec->noinstr)
2709 return true;
2710
2711 /*
2712 * The __ubsan_handle_*() calls are like WARN(), they only happen when
2713 * something 'BAD' happened. At the risk of taking the machine down,
2714 * let them proceed to get the message out.
2715 */
2716 if (!strncmp(func->name, "__ubsan_handle_", 15))
2717 return true;
2718
2719 return false;
2720}
2721
David Brazdil0f672f62019-12-10 10:32:29 +00002722static int validate_call(struct instruction *insn, struct insn_state *state)
2723{
Olivier Deprez157378f2022-04-04 15:47:50 +02002724 if (state->noinstr && state->instr <= 0 &&
2725 !noinstr_call_dest(insn->call_dest)) {
2726 WARN_FUNC("call to %s() leaves .noinstr.text section",
2727 insn->sec, insn->offset, call_dest_name(insn));
2728 return 1;
2729 }
2730
David Brazdil0f672f62019-12-10 10:32:29 +00002731 if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
2732 WARN_FUNC("call to %s() with UACCESS enabled",
2733 insn->sec, insn->offset, call_dest_name(insn));
2734 return 1;
2735 }
2736
2737 if (state->df) {
2738 WARN_FUNC("call to %s() with DF set",
2739 insn->sec, insn->offset, call_dest_name(insn));
2740 return 1;
2741 }
2742
2743 return 0;
2744}
2745
2746static int validate_sibling_call(struct instruction *insn, struct insn_state *state)
2747{
Olivier Deprez157378f2022-04-04 15:47:50 +02002748 if (has_modified_stack_frame(insn, state)) {
David Brazdil0f672f62019-12-10 10:32:29 +00002749 WARN_FUNC("sibling call from callable instruction with modified stack frame",
2750 insn->sec, insn->offset);
2751 return 1;
2752 }
2753
2754 return validate_call(insn, state);
2755}
2756
Olivier Deprez157378f2022-04-04 15:47:50 +02002757static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
2758{
2759 if (state->noinstr && state->instr > 0) {
2760 WARN_FUNC("return with instrumentation enabled",
2761 insn->sec, insn->offset);
2762 return 1;
2763 }
2764
2765 if (state->uaccess && !func_uaccess_safe(func)) {
2766 WARN_FUNC("return with UACCESS enabled",
2767 insn->sec, insn->offset);
2768 return 1;
2769 }
2770
2771 if (!state->uaccess && func_uaccess_safe(func)) {
2772 WARN_FUNC("return with UACCESS disabled from a UACCESS-safe function",
2773 insn->sec, insn->offset);
2774 return 1;
2775 }
2776
2777 if (state->df) {
2778 WARN_FUNC("return with DF set",
2779 insn->sec, insn->offset);
2780 return 1;
2781 }
2782
2783 if (func && has_modified_stack_frame(insn, state)) {
2784 WARN_FUNC("return with modified stack frame",
2785 insn->sec, insn->offset);
2786 return 1;
2787 }
2788
2789 if (state->cfi.bp_scratch) {
2790 WARN_FUNC("BP used as a scratch register",
2791 insn->sec, insn->offset);
2792 return 1;
2793 }
2794
2795 return 0;
2796}
2797
Olivier Deprez92d4c212022-12-06 15:05:30 +01002798static struct instruction *next_insn_to_validate(struct objtool_file *file,
2799 struct instruction *insn)
Olivier Deprez157378f2022-04-04 15:47:50 +02002800{
Olivier Deprez92d4c212022-12-06 15:05:30 +01002801 struct alt_group *alt_group = insn->alt_group;
Olivier Deprez157378f2022-04-04 15:47:50 +02002802
Olivier Deprez92d4c212022-12-06 15:05:30 +01002803 /*
2804 * Simulate the fact that alternatives are patched in-place. When the
2805 * end of a replacement alt_group is reached, redirect objtool flow to
2806 * the end of the original alt_group.
2807 */
2808 if (alt_group && insn == alt_group->last_insn && alt_group->orig_group)
2809 return next_insn_same_sec(file, alt_group->orig_group->last_insn);
2810
2811 return next_insn_same_sec(file, insn);
Olivier Deprez157378f2022-04-04 15:47:50 +02002812}
2813
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002814/*
2815 * Follow the branch starting at the given instruction, and recursively follow
2816 * any other branches (jumps). Meanwhile, track the frame pointer state at
2817 * each instruction and validate all the rules described in
2818 * tools/objtool/Documentation/stack-validation.txt.
2819 */
David Brazdil0f672f62019-12-10 10:32:29 +00002820static int validate_branch(struct objtool_file *file, struct symbol *func,
Olivier Deprez157378f2022-04-04 15:47:50 +02002821 struct instruction *insn, struct insn_state state)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002822{
2823 struct alternative *alt;
Olivier Deprez92d4c212022-12-06 15:05:30 +01002824 struct instruction *next_insn, *prev_insn = NULL;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002825 struct section *sec;
David Brazdil0f672f62019-12-10 10:32:29 +00002826 u8 visited;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002827 int ret;
2828
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002829 sec = insn->sec;
2830
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002831 while (1) {
Olivier Deprez92d4c212022-12-06 15:05:30 +01002832 next_insn = next_insn_to_validate(file, insn);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002833
2834 if (file->c_file && func && insn->func && func != insn->func->pfunc) {
2835 WARN("%s() falls through to next function %s()",
2836 func->name, insn->func->name);
2837 return 1;
2838 }
2839
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002840 if (func && insn->ignore) {
2841 WARN_FUNC("BUG: why am I validating an ignored function?",
2842 sec, insn->offset);
2843 return 1;
2844 }
2845
Olivier Deprez92d4c212022-12-06 15:05:30 +01002846 visited = VISITED_BRANCH << state.uaccess;
2847 if (insn->visited & VISITED_BRANCH_MASK) {
Olivier Deprez157378f2022-04-04 15:47:50 +02002848 if (!insn->hint && !insn_cfi_match(insn, &state.cfi))
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002849 return 1;
2850
David Brazdil0f672f62019-12-10 10:32:29 +00002851 if (insn->visited & visited)
2852 return 0;
Olivier Deprez92d4c212022-12-06 15:05:30 +01002853 } else {
2854 nr_insns_visited++;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002855 }
2856
Olivier Deprez157378f2022-04-04 15:47:50 +02002857 if (state.noinstr)
2858 state.instr += insn->instr;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002859
Olivier Deprez92d4c212022-12-06 15:05:30 +01002860 if (insn->hint) {
2861 if (insn->restore) {
2862 struct instruction *save_insn, *i;
2863
2864 i = insn;
2865 save_insn = NULL;
2866
2867 sym_for_each_insn_continue_reverse(file, func, i) {
2868 if (i->save) {
2869 save_insn = i;
2870 break;
2871 }
2872 }
2873
2874 if (!save_insn) {
2875 WARN_FUNC("no corresponding CFI save for CFI restore",
2876 sec, insn->offset);
2877 return 1;
2878 }
2879
2880 if (!save_insn->visited) {
2881 WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
2882 sec, insn->offset);
2883 return 1;
2884 }
2885
2886 insn->cfi = save_insn->cfi;
2887 nr_cfi_reused++;
2888 }
2889
2890 state.cfi = *insn->cfi;
2891 } else {
2892 /* XXX track if we actually changed state.cfi */
2893
2894 if (prev_insn && !cficmp(prev_insn->cfi, &state.cfi)) {
2895 insn->cfi = prev_insn->cfi;
2896 nr_cfi_reused++;
2897 } else {
2898 insn->cfi = cfi_hash_find_or_add(&state.cfi);
2899 }
2900 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002901
David Brazdil0f672f62019-12-10 10:32:29 +00002902 insn->visited |= visited;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002903
Olivier Deprez92d4c212022-12-06 15:05:30 +01002904 if (propagate_alt_cfi(file, insn))
2905 return 1;
2906
Olivier Deprez157378f2022-04-04 15:47:50 +02002907 if (!insn->ignore_alts && !list_empty(&insn->alts)) {
David Brazdil0f672f62019-12-10 10:32:29 +00002908 bool skip_orig = false;
2909
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002910 list_for_each_entry(alt, &insn->alts, list) {
David Brazdil0f672f62019-12-10 10:32:29 +00002911 if (alt->skip_orig)
2912 skip_orig = true;
2913
2914 ret = validate_branch(file, func, alt->insn, state);
2915 if (ret) {
2916 if (backtrace)
2917 BT_FUNC("(alt)", insn);
2918 return ret;
2919 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002920 }
David Brazdil0f672f62019-12-10 10:32:29 +00002921
2922 if (skip_orig)
2923 return 0;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002924 }
2925
Olivier Deprez157378f2022-04-04 15:47:50 +02002926 if (handle_insn_ops(insn, &state))
2927 return 1;
2928
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002929 switch (insn->type) {
2930
2931 case INSN_RETURN:
Olivier Deprez92d4c212022-12-06 15:05:30 +01002932 if (sls && !insn->retpoline_safe &&
2933 next_insn && next_insn->type != INSN_TRAP) {
2934 WARN_FUNC("missing int3 after ret",
2935 insn->sec, insn->offset);
2936 }
Olivier Deprez157378f2022-04-04 15:47:50 +02002937 return validate_return(func, insn, &state);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002938
2939 case INSN_CALL:
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002940 case INSN_CALL_DYNAMIC:
David Brazdil0f672f62019-12-10 10:32:29 +00002941 ret = validate_call(insn, &state);
2942 if (ret)
2943 return ret;
2944
2945 if (!no_fp && func && !is_fentry_call(insn) &&
2946 !has_valid_stack_frame(&state)) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002947 WARN_FUNC("call without frame pointer save/setup",
2948 sec, insn->offset);
2949 return 1;
2950 }
David Brazdil0f672f62019-12-10 10:32:29 +00002951
2952 if (dead_end_function(file, insn->call_dest))
2953 return 0;
2954
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002955 break;
2956
2957 case INSN_JUMP_CONDITIONAL:
2958 case INSN_JUMP_UNCONDITIONAL:
Olivier Deprez92d4c212022-12-06 15:05:30 +01002959 if (is_sibling_call(insn)) {
David Brazdil0f672f62019-12-10 10:32:29 +00002960 ret = validate_sibling_call(insn, &state);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002961 if (ret)
David Brazdil0f672f62019-12-10 10:32:29 +00002962 return ret;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002963
David Brazdil0f672f62019-12-10 10:32:29 +00002964 } else if (insn->jump_dest) {
2965 ret = validate_branch(file, func,
2966 insn->jump_dest, state);
2967 if (ret) {
2968 if (backtrace)
2969 BT_FUNC("(branch)", insn);
2970 return ret;
2971 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002972 }
2973
2974 if (insn->type == INSN_JUMP_UNCONDITIONAL)
2975 return 0;
2976
2977 break;
2978
2979 case INSN_JUMP_DYNAMIC:
Olivier Deprez92d4c212022-12-06 15:05:30 +01002980 if (sls && !insn->retpoline_safe &&
2981 next_insn && next_insn->type != INSN_TRAP) {
2982 WARN_FUNC("missing int3 after indirect jump",
2983 insn->sec, insn->offset);
2984 }
2985
2986 /* fallthrough */
David Brazdil0f672f62019-12-10 10:32:29 +00002987 case INSN_JUMP_DYNAMIC_CONDITIONAL:
Olivier Deprez92d4c212022-12-06 15:05:30 +01002988 if (is_sibling_call(insn)) {
David Brazdil0f672f62019-12-10 10:32:29 +00002989 ret = validate_sibling_call(insn, &state);
2990 if (ret)
2991 return ret;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002992 }
2993
David Brazdil0f672f62019-12-10 10:32:29 +00002994 if (insn->type == INSN_JUMP_DYNAMIC)
2995 return 0;
2996
2997 break;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002998
2999 case INSN_CONTEXT_SWITCH:
3000 if (func && (!next_insn || !next_insn->hint)) {
3001 WARN_FUNC("unsupported instruction in callable function",
3002 sec, insn->offset);
3003 return 1;
3004 }
3005 return 0;
3006
David Brazdil0f672f62019-12-10 10:32:29 +00003007 case INSN_STAC:
3008 if (state.uaccess) {
3009 WARN_FUNC("recursive UACCESS enable", sec, insn->offset);
3010 return 1;
3011 }
3012
3013 state.uaccess = true;
3014 break;
3015
3016 case INSN_CLAC:
3017 if (!state.uaccess && func) {
3018 WARN_FUNC("redundant UACCESS disable", sec, insn->offset);
3019 return 1;
3020 }
3021
3022 if (func_uaccess_safe(func) && !state.uaccess_stack) {
3023 WARN_FUNC("UACCESS-safe disables UACCESS", sec, insn->offset);
3024 return 1;
3025 }
3026
3027 state.uaccess = false;
3028 break;
3029
3030 case INSN_STD:
Olivier Deprez0e641232021-09-23 10:07:05 +02003031 if (state.df) {
David Brazdil0f672f62019-12-10 10:32:29 +00003032 WARN_FUNC("recursive STD", sec, insn->offset);
Olivier Deprez0e641232021-09-23 10:07:05 +02003033 return 1;
3034 }
David Brazdil0f672f62019-12-10 10:32:29 +00003035
3036 state.df = true;
3037 break;
3038
3039 case INSN_CLD:
Olivier Deprez0e641232021-09-23 10:07:05 +02003040 if (!state.df && func) {
David Brazdil0f672f62019-12-10 10:32:29 +00003041 WARN_FUNC("redundant CLD", sec, insn->offset);
Olivier Deprez0e641232021-09-23 10:07:05 +02003042 return 1;
3043 }
David Brazdil0f672f62019-12-10 10:32:29 +00003044
3045 state.df = false;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003046 break;
3047
3048 default:
3049 break;
3050 }
3051
3052 if (insn->dead_end)
3053 return 0;
3054
3055 if (!next_insn) {
Olivier Deprez157378f2022-04-04 15:47:50 +02003056 if (state.cfi.cfa.base == CFI_UNDEFINED)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003057 return 0;
3058 WARN("%s: unexpected end of section", sec->name);
3059 return 1;
3060 }
3061
Olivier Deprez92d4c212022-12-06 15:05:30 +01003062 prev_insn = insn;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003063 insn = next_insn;
3064 }
3065
3066 return 0;
3067}
3068
Olivier Deprez157378f2022-04-04 15:47:50 +02003069static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003070{
3071 struct instruction *insn;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003072 struct insn_state state;
Olivier Deprez157378f2022-04-04 15:47:50 +02003073 int ret, warnings = 0;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003074
3075 if (!file->hints)
3076 return 0;
3077
Olivier Deprez157378f2022-04-04 15:47:50 +02003078 init_insn_state(&state, sec);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003079
Olivier Deprez157378f2022-04-04 15:47:50 +02003080 if (sec) {
3081 insn = find_insn(file, sec, 0);
3082 if (!insn)
3083 return 0;
3084 } else {
3085 insn = list_first_entry(&file->insn_list, typeof(*insn), list);
3086 }
3087
3088 while (&insn->list != &file->insn_list && (!sec || insn->sec == sec)) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003089 if (insn->hint && !insn->visited) {
David Brazdil0f672f62019-12-10 10:32:29 +00003090 ret = validate_branch(file, insn->func, insn, state);
3091 if (ret && backtrace)
3092 BT_FUNC("<=== (hint)", insn);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003093 warnings += ret;
3094 }
Olivier Deprez157378f2022-04-04 15:47:50 +02003095
3096 insn = list_next_entry(insn, list);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003097 }
3098
3099 return warnings;
3100}
3101
Olivier Deprez92d4c212022-12-06 15:05:30 +01003102/*
3103 * Validate rethunk entry constraint: must untrain RET before the first RET.
3104 *
3105 * Follow every branch (intra-function) and ensure ANNOTATE_UNRET_END comes
3106 * before an actual RET instruction.
3107 */
3108static int validate_entry(struct objtool_file *file, struct instruction *insn)
3109{
3110 struct instruction *next, *dest;
3111 int ret, warnings = 0;
3112
3113 for (;;) {
3114 next = next_insn_to_validate(file, insn);
3115
3116 if (insn->visited & VISITED_ENTRY)
3117 return 0;
3118
3119 insn->visited |= VISITED_ENTRY;
3120
3121 if (!insn->ignore_alts && !list_empty(&insn->alts)) {
3122 struct alternative *alt;
3123 bool skip_orig = false;
3124
3125 list_for_each_entry(alt, &insn->alts, list) {
3126 if (alt->skip_orig)
3127 skip_orig = true;
3128
3129 ret = validate_entry(file, alt->insn);
3130 if (ret) {
3131 if (backtrace)
3132 BT_FUNC("(alt)", insn);
3133 return ret;
3134 }
3135 }
3136
3137 if (skip_orig)
3138 return 0;
3139 }
3140
3141 switch (insn->type) {
3142
3143 case INSN_CALL_DYNAMIC:
3144 case INSN_JUMP_DYNAMIC:
3145 case INSN_JUMP_DYNAMIC_CONDITIONAL:
3146 WARN_FUNC("early indirect call", insn->sec, insn->offset);
3147 return 1;
3148
3149 case INSN_JUMP_UNCONDITIONAL:
3150 case INSN_JUMP_CONDITIONAL:
3151 if (!is_sibling_call(insn)) {
3152 if (!insn->jump_dest) {
3153 WARN_FUNC("unresolved jump target after linking?!?",
3154 insn->sec, insn->offset);
3155 return -1;
3156 }
3157 ret = validate_entry(file, insn->jump_dest);
3158 if (ret) {
3159 if (backtrace) {
3160 BT_FUNC("(branch%s)", insn,
3161 insn->type == INSN_JUMP_CONDITIONAL ? "-cond" : "");
3162 }
3163 return ret;
3164 }
3165
3166 if (insn->type == INSN_JUMP_UNCONDITIONAL)
3167 return 0;
3168
3169 break;
3170 }
3171
3172 /* fallthrough */
3173 case INSN_CALL:
3174 dest = find_insn(file, insn->call_dest->sec,
3175 insn->call_dest->offset);
3176 if (!dest) {
3177 WARN("Unresolved function after linking!?: %s",
3178 insn->call_dest->name);
3179 return -1;
3180 }
3181
3182 ret = validate_entry(file, dest);
3183 if (ret) {
3184 if (backtrace)
3185 BT_FUNC("(call)", insn);
3186 return ret;
3187 }
3188 /*
3189 * If a call returns without error, it must have seen UNTRAIN_RET.
3190 * Therefore any non-error return is a success.
3191 */
3192 return 0;
3193
3194 case INSN_RETURN:
3195 WARN_FUNC("RET before UNTRAIN", insn->sec, insn->offset);
3196 return 1;
3197
3198 case INSN_NOP:
3199 if (insn->retpoline_safe)
3200 return 0;
3201 break;
3202
3203 default:
3204 break;
3205 }
3206
3207 if (!next) {
3208 WARN_FUNC("teh end!", insn->sec, insn->offset);
3209 return -1;
3210 }
3211 insn = next;
3212 }
3213
3214 return warnings;
3215}
3216
3217/*
3218 * Validate that all branches starting at 'insn->entry' encounter UNRET_END
3219 * before RET.
3220 */
3221static int validate_unret(struct objtool_file *file)
3222{
3223 struct instruction *insn;
3224 int ret, warnings = 0;
3225
3226 for_each_insn(file, insn) {
3227 if (!insn->entry)
3228 continue;
3229
3230 ret = validate_entry(file, insn);
3231 if (ret < 0) {
3232 WARN_FUNC("Failed UNRET validation", insn->sec, insn->offset);
3233 return ret;
3234 }
3235 warnings += ret;
3236 }
3237
3238 return warnings;
3239}
3240
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003241static int validate_retpoline(struct objtool_file *file)
3242{
3243 struct instruction *insn;
3244 int warnings = 0;
3245
3246 for_each_insn(file, insn) {
3247 if (insn->type != INSN_JUMP_DYNAMIC &&
Olivier Deprez92d4c212022-12-06 15:05:30 +01003248 insn->type != INSN_CALL_DYNAMIC &&
3249 insn->type != INSN_RETURN)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003250 continue;
3251
3252 if (insn->retpoline_safe)
3253 continue;
3254
3255 /*
3256 * .init.text code is ran before userspace and thus doesn't
3257 * strictly need retpolines, except for modules which are
3258 * loaded late, they very much do need retpoline in their
3259 * .init.text
3260 */
3261 if (!strcmp(insn->sec->name, ".init.text") && !module)
3262 continue;
3263
Olivier Deprez92d4c212022-12-06 15:05:30 +01003264 if (insn->type == INSN_RETURN) {
3265 if (rethunk) {
3266 WARN_FUNC("'naked' return found in RETHUNK build",
3267 insn->sec, insn->offset);
3268 } else
3269 continue;
3270 } else {
3271 WARN_FUNC("indirect %s found in RETPOLINE build",
3272 insn->sec, insn->offset,
3273 insn->type == INSN_JUMP_DYNAMIC ? "jump" : "call");
3274 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003275
3276 warnings++;
3277 }
3278
3279 return warnings;
3280}
3281
3282static bool is_kasan_insn(struct instruction *insn)
3283{
3284 return (insn->type == INSN_CALL &&
3285 !strcmp(insn->call_dest->name, "__asan_handle_no_return"));
3286}
3287
3288static bool is_ubsan_insn(struct instruction *insn)
3289{
3290 return (insn->type == INSN_CALL &&
3291 !strcmp(insn->call_dest->name,
3292 "__ubsan_handle_builtin_unreachable"));
3293}
3294
Olivier Deprez157378f2022-04-04 15:47:50 +02003295static bool ignore_unreachable_insn(struct objtool_file *file, struct instruction *insn)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003296{
3297 int i;
Olivier Deprez157378f2022-04-04 15:47:50 +02003298 struct instruction *prev_insn;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003299
Olivier Deprez92d4c212022-12-06 15:05:30 +01003300 if (insn->ignore || insn->type == INSN_NOP || insn->type == INSN_TRAP)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003301 return true;
3302
3303 /*
3304 * Ignore any unused exceptions. This can happen when a whitelisted
3305 * function has an exception table entry.
3306 *
3307 * Also ignore alternative replacement instructions. This can happen
3308 * when a whitelisted function uses one of the ALTERNATIVE macros.
3309 */
3310 if (!strcmp(insn->sec->name, ".fixup") ||
3311 !strcmp(insn->sec->name, ".altinstr_replacement") ||
3312 !strcmp(insn->sec->name, ".altinstr_aux"))
3313 return true;
3314
Olivier Deprez0e641232021-09-23 10:07:05 +02003315 if (!insn->func)
3316 return false;
3317
3318 /*
3319 * CONFIG_UBSAN_TRAP inserts a UD2 when it sees
3320 * __builtin_unreachable(). The BUG() macro has an unreachable() after
3321 * the UD2, which causes GCC's undefined trap logic to emit another UD2
3322 * (or occasionally a JMP to UD2).
Olivier Deprez157378f2022-04-04 15:47:50 +02003323 *
3324 * It may also insert a UD2 after calling a __noreturn function.
Olivier Deprez0e641232021-09-23 10:07:05 +02003325 */
Olivier Deprez157378f2022-04-04 15:47:50 +02003326 prev_insn = list_prev_entry(insn, list);
3327 if ((prev_insn->dead_end || dead_end_function(file, prev_insn->call_dest)) &&
Olivier Deprez0e641232021-09-23 10:07:05 +02003328 (insn->type == INSN_BUG ||
3329 (insn->type == INSN_JUMP_UNCONDITIONAL &&
3330 insn->jump_dest && insn->jump_dest->type == INSN_BUG)))
3331 return true;
3332
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003333 /*
3334 * Check if this (or a subsequent) instruction is related to
3335 * CONFIG_UBSAN or CONFIG_KASAN.
3336 *
3337 * End the search at 5 instructions to avoid going into the weeds.
3338 */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003339 for (i = 0; i < 5; i++) {
3340
3341 if (is_kasan_insn(insn) || is_ubsan_insn(insn))
3342 return true;
3343
3344 if (insn->type == INSN_JUMP_UNCONDITIONAL) {
3345 if (insn->jump_dest &&
3346 insn->jump_dest->func == insn->func) {
3347 insn = insn->jump_dest;
3348 continue;
3349 }
3350
3351 break;
3352 }
3353
3354 if (insn->offset + insn->len >= insn->func->offset + insn->func->len)
3355 break;
3356
3357 insn = list_next_entry(insn, list);
3358 }
3359
3360 return false;
3361}
3362
Olivier Deprez157378f2022-04-04 15:47:50 +02003363static int validate_symbol(struct objtool_file *file, struct section *sec,
3364 struct symbol *sym, struct insn_state *state)
3365{
3366 struct instruction *insn;
3367 int ret;
3368
3369 if (!sym->len) {
3370 WARN("%s() is missing an ELF size annotation", sym->name);
3371 return 1;
3372 }
3373
3374 if (sym->pfunc != sym || sym->alias != sym)
3375 return 0;
3376
3377 insn = find_insn(file, sec, sym->offset);
3378 if (!insn || insn->ignore || insn->visited)
3379 return 0;
3380
3381 state->uaccess = sym->uaccess_safe;
3382
3383 ret = validate_branch(file, insn->func, insn, *state);
3384 if (ret && backtrace)
3385 BT_FUNC("<=== (sym)", insn);
3386 return ret;
3387}
3388
3389static int validate_section(struct objtool_file *file, struct section *sec)
3390{
3391 struct insn_state state;
3392 struct symbol *func;
3393 int warnings = 0;
3394
3395 list_for_each_entry(func, &sec->symbol_list, list) {
3396 if (func->type != STT_FUNC)
3397 continue;
3398
3399 init_insn_state(&state, sec);
Olivier Deprez92d4c212022-12-06 15:05:30 +01003400 set_func_state(&state.cfi);
Olivier Deprez157378f2022-04-04 15:47:50 +02003401
3402 warnings += validate_symbol(file, sec, func, &state);
3403 }
3404
3405 return warnings;
3406}
3407
3408static int validate_vmlinux_functions(struct objtool_file *file)
3409{
3410 struct section *sec;
3411 int warnings = 0;
3412
3413 sec = find_section_by_name(file->elf, ".noinstr.text");
3414 if (sec) {
3415 warnings += validate_section(file, sec);
3416 warnings += validate_unwind_hints(file, sec);
3417 }
3418
3419 sec = find_section_by_name(file->elf, ".entry.text");
3420 if (sec) {
3421 warnings += validate_section(file, sec);
3422 warnings += validate_unwind_hints(file, sec);
3423 }
3424
3425 return warnings;
3426}
3427
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003428static int validate_functions(struct objtool_file *file)
3429{
3430 struct section *sec;
Olivier Deprez157378f2022-04-04 15:47:50 +02003431 int warnings = 0;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003432
3433 for_each_sec(file, sec) {
Olivier Deprez157378f2022-04-04 15:47:50 +02003434 if (!(sec->sh.sh_flags & SHF_EXECINSTR))
3435 continue;
David Brazdil0f672f62019-12-10 10:32:29 +00003436
Olivier Deprez157378f2022-04-04 15:47:50 +02003437 warnings += validate_section(file, sec);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003438 }
3439
3440 return warnings;
3441}
3442
3443static int validate_reachable_instructions(struct objtool_file *file)
3444{
3445 struct instruction *insn;
3446
3447 if (file->ignore_unreachables)
3448 return 0;
3449
3450 for_each_insn(file, insn) {
Olivier Deprez157378f2022-04-04 15:47:50 +02003451 if (insn->visited || ignore_unreachable_insn(file, insn))
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003452 continue;
3453
3454 WARN_FUNC("unreachable instruction", insn->sec, insn->offset);
3455 return 1;
3456 }
3457
3458 return 0;
3459}
3460
Olivier Deprez157378f2022-04-04 15:47:50 +02003461int check(struct objtool_file *file)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003462{
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003463 int ret, warnings = 0;
3464
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003465 arch_initial_func_cfi_state(&initial_func_cfi);
Olivier Deprez92d4c212022-12-06 15:05:30 +01003466 init_cfi_state(&init_cfi);
3467 init_cfi_state(&func_cfi);
3468 set_func_state(&func_cfi);
3469
3470 if (!cfi_hash_alloc())
3471 goto out;
3472
3473 cfi_hash_add(&init_cfi);
3474 cfi_hash_add(&func_cfi);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003475
Olivier Deprez157378f2022-04-04 15:47:50 +02003476 ret = decode_sections(file);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003477 if (ret < 0)
3478 goto out;
Olivier Deprez92d4c212022-12-06 15:05:30 +01003479
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003480 warnings += ret;
3481
Olivier Deprez157378f2022-04-04 15:47:50 +02003482 if (list_empty(&file->insn_list))
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003483 goto out;
3484
Olivier Deprez157378f2022-04-04 15:47:50 +02003485 if (vmlinux && !validate_dup) {
3486 ret = validate_vmlinux_functions(file);
3487 if (ret < 0)
3488 goto out;
3489
3490 warnings += ret;
3491 goto out;
3492 }
3493
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003494 if (retpoline) {
Olivier Deprez157378f2022-04-04 15:47:50 +02003495 ret = validate_retpoline(file);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003496 if (ret < 0)
3497 return ret;
3498 warnings += ret;
3499 }
3500
Olivier Deprez157378f2022-04-04 15:47:50 +02003501 ret = validate_functions(file);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003502 if (ret < 0)
3503 goto out;
3504 warnings += ret;
3505
Olivier Deprez157378f2022-04-04 15:47:50 +02003506 ret = validate_unwind_hints(file, NULL);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003507 if (ret < 0)
3508 goto out;
3509 warnings += ret;
3510
Olivier Deprez92d4c212022-12-06 15:05:30 +01003511 if (unret) {
3512 /*
3513 * Must be after validate_branch() and friends, it plays
3514 * further games with insn->visited.
3515 */
3516 ret = validate_unret(file);
3517 if (ret < 0)
3518 return ret;
3519 warnings += ret;
3520 }
3521
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003522 if (!warnings) {
Olivier Deprez157378f2022-04-04 15:47:50 +02003523 ret = validate_reachable_instructions(file);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003524 if (ret < 0)
3525 goto out;
3526 warnings += ret;
3527 }
3528
Olivier Deprez157378f2022-04-04 15:47:50 +02003529 ret = create_static_call_sections(file);
3530 if (ret < 0)
3531 goto out;
3532 warnings += ret;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003533
Olivier Deprez92d4c212022-12-06 15:05:30 +01003534 if (retpoline) {
3535 ret = create_retpoline_sites_sections(file);
3536 if (ret < 0)
3537 goto out;
3538 warnings += ret;
3539 }
3540
3541 if (rethunk) {
3542 ret = create_return_sites_sections(file);
3543 if (ret < 0)
3544 goto out;
3545 warnings += ret;
3546 }
3547
3548 if (stats) {
3549 printf("nr_insns_visited: %ld\n", nr_insns_visited);
3550 printf("nr_cfi: %ld\n", nr_cfi);
3551 printf("nr_cfi_reused: %ld\n", nr_cfi_reused);
3552 printf("nr_cfi_cache: %ld\n", nr_cfi_cache);
3553 }
3554
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003555out:
Olivier Deprez157378f2022-04-04 15:47:50 +02003556 /*
3557 * For now, don't fail the kernel build on fatal warnings. These
3558 * errors are still fairly common due to the growing matrix of
3559 * supported toolchains and their recent pace of change.
3560 */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00003561 return 0;
3562}