blob: f2a84271e8073930ba098f1e54419efb6ead5f6f [file] [log] [blame]
Thomas Gleixner1ccea772019-05-19 15:51:43 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002/*
3 * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05004 */
5
6#include <string.h>
7#include <stdlib.h>
8
Peter Zijlstra43a45252018-01-16 17:16:32 +01009#include "builtin.h"
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050010#include "check.h"
11#include "elf.h"
12#include "special.h"
13#include "arch.h"
14#include "warn.h"
15
16#include <linux/hashtable.h>
17#include <linux/kernel.h>
18
Josh Poimboeufe6da9562019-05-13 12:01:31 -050019#define FAKE_JUMP_OFFSET -1
20
Josh Poimboeuf87b512d2019-06-27 20:50:46 -050021#define C_JUMP_TABLE_SECTION ".rodata..c_jump_table"
22
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050023struct alternative {
24 struct list_head list;
25 struct instruction *insn;
Peter Zijlstra764eef42019-03-01 11:19:03 +010026 bool skip_orig;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050027};
28
29const char *objname;
Peter Zijlstraa3608f52020-03-25 15:34:50 +010030struct cfi_init_state initial_func_cfi;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050031
Josh Poimboeuf627fce12017-07-11 10:33:42 -050032struct instruction *find_insn(struct objtool_file *file,
33 struct section *sec, unsigned long offset)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050034{
35 struct instruction *insn;
36
Peter Zijlstra87ecb582020-03-16 15:47:27 +010037 hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, offset)) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050038 if (insn->sec == sec && insn->offset == offset)
39 return insn;
Peter Zijlstra87ecb582020-03-16 15:47:27 +010040 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050041
42 return NULL;
43}
44
45static struct instruction *next_insn_same_sec(struct objtool_file *file,
46 struct instruction *insn)
47{
48 struct instruction *next = list_next_entry(insn, list);
49
Josh Poimboeufbaa41462017-06-28 10:11:07 -050050 if (!next || &next->list == &file->insn_list || next->sec != insn->sec)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050051 return NULL;
52
53 return next;
54}
55
Josh Poimboeuf13810432018-05-09 22:39:15 -050056static struct instruction *next_insn_same_func(struct objtool_file *file,
57 struct instruction *insn)
58{
59 struct instruction *next = list_next_entry(insn, list);
60 struct symbol *func = insn->func;
61
62 if (!func)
63 return NULL;
64
65 if (&next->list != &file->insn_list && next->func == func)
66 return next;
67
68 /* Check if we're already in the subfunction: */
69 if (func == func->cfunc)
70 return NULL;
71
72 /* Move to the subfunction: */
73 return find_insn(file, func->cfunc->sec, func->cfunc->offset);
74}
75
Peter Zijlstraf0f70ad2020-03-10 18:27:24 +010076#define func_for_each_insn(file, func, insn) \
Josh Poimboeuf13810432018-05-09 22:39:15 -050077 for (insn = find_insn(file, func->sec, func->offset); \
78 insn; \
79 insn = next_insn_same_func(file, insn))
80
Peter Zijlstradbf4aeb2020-03-10 18:24:59 +010081#define sym_for_each_insn(file, sym, insn) \
82 for (insn = find_insn(file, sym->sec, sym->offset); \
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050083 insn && &insn->list != &file->insn_list && \
Peter Zijlstradbf4aeb2020-03-10 18:24:59 +010084 insn->sec == sym->sec && \
85 insn->offset < sym->offset + sym->len; \
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050086 insn = list_next_entry(insn, list))
87
Peter Zijlstradbf4aeb2020-03-10 18:24:59 +010088#define sym_for_each_insn_continue_reverse(file, sym, insn) \
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050089 for (insn = list_prev_entry(insn, list); \
90 &insn->list != &file->insn_list && \
Peter Zijlstradbf4aeb2020-03-10 18:24:59 +010091 insn->sec == sym->sec && insn->offset >= sym->offset; \
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050092 insn = list_prev_entry(insn, list))
93
94#define sec_for_each_insn_from(file, insn) \
95 for (; insn; insn = next_insn_same_sec(file, insn))
96
Josh Poimboeufbaa41462017-06-28 10:11:07 -050097#define sec_for_each_insn_continue(file, insn) \
98 for (insn = next_insn_same_sec(file, insn); insn; \
99 insn = next_insn_same_sec(file, insn))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500100
Josh Poimboeufa2296142020-02-10 12:32:39 -0600101static bool is_static_jump(struct instruction *insn)
102{
103 return insn->type == INSN_JUMP_CONDITIONAL ||
104 insn->type == INSN_JUMP_UNCONDITIONAL;
105}
106
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -0500107static bool is_sibling_call(struct instruction *insn)
108{
109 /* An indirect jump is either a sibling call or a jump to a table. */
110 if (insn->type == INSN_JUMP_DYNAMIC)
111 return list_empty(&insn->alts);
112
Josh Poimboeufa2296142020-02-10 12:32:39 -0600113 if (!is_static_jump(insn))
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -0500114 return false;
115
116 /* add_jump_destinations() sets insn->call_dest for sibling calls. */
117 return !!insn->call_dest;
118}
119
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500120/*
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500121 * This checks to see if the given function is a "noreturn" function.
122 *
123 * For global functions which are outside the scope of this object file, we
124 * have to keep a manual list of them.
125 *
126 * For local functions, we have to detect them manually by simply looking for
127 * the lack of a return instruction.
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500128 */
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500129static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
130 int recursion)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500131{
132 int i;
133 struct instruction *insn;
134 bool empty = true;
135
136 /*
137 * Unfortunately these have to be hard coded because the noreturn
138 * attribute isn't provided in ELF data.
139 */
140 static const char * const global_noreturns[] = {
141 "__stack_chk_fail",
142 "panic",
143 "do_exit",
144 "do_task_dead",
145 "__module_put_and_exit",
146 "complete_and_exit",
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500147 "__reiserfs_panic",
148 "lbug_with_loc",
149 "fortify_panic",
Kees Cookb394d462018-01-10 14:22:38 -0800150 "usercopy_abort",
Josh Poimboeuf684fb242018-06-19 10:47:50 -0500151 "machine_real_restart",
Josh Poimboeuf4fa5ecd2019-04-04 12:17:35 -0500152 "rewind_stack_do_exit",
Brendan Higgins33adf802019-09-23 02:02:38 -0700153 "kunit_try_catch_throw",
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500154 };
155
Josh Poimboeufc9bab222019-07-17 20:36:51 -0500156 if (!func)
157 return false;
158
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500159 if (func->bind == STB_WEAK)
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500160 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500161
162 if (func->bind == STB_GLOBAL)
163 for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
164 if (!strcmp(func->name, global_noreturns[i]))
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500165 return true;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500166
Josh Poimboeuf13810432018-05-09 22:39:15 -0500167 if (!func->len)
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500168 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500169
Josh Poimboeuf13810432018-05-09 22:39:15 -0500170 insn = find_insn(file, func->sec, func->offset);
171 if (!insn->func)
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500172 return false;
Josh Poimboeuf13810432018-05-09 22:39:15 -0500173
Peter Zijlstraf0f70ad2020-03-10 18:27:24 +0100174 func_for_each_insn(file, func, insn) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500175 empty = false;
176
177 if (insn->type == INSN_RETURN)
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500178 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500179 }
180
181 if (empty)
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500182 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500183
184 /*
185 * A function can have a sibling call instead of a return. In that
186 * case, the function's dead-end status depends on whether the target
187 * of the sibling call returns.
188 */
Peter Zijlstraf0f70ad2020-03-10 18:27:24 +0100189 func_for_each_insn(file, func, insn) {
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -0500190 if (is_sibling_call(insn)) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500191 struct instruction *dest = insn->jump_dest;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500192
193 if (!dest)
194 /* sibling call to another file */
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500195 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500196
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -0500197 /* local sibling call */
198 if (recursion == 5) {
199 /*
200 * Infinite recursion: two functions have
201 * sibling calls to each other. This is a very
202 * rare case. It means they aren't dead ends.
203 */
204 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500205 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500206
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -0500207 return __dead_end_function(file, dest->func, recursion+1);
208 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500209 }
210
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500211 return true;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500212}
213
Josh Poimboeuf8e25c9f2019-07-17 20:36:50 -0500214static bool dead_end_function(struct objtool_file *file, struct symbol *func)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500215{
216 return __dead_end_function(file, func, 0);
217}
218
Peter Zijlstrae7c02192020-03-25 14:04:45 +0100219static void init_cfi_state(struct cfi_state *cfi)
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500220{
221 int i;
222
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -0500223 for (i = 0; i < CFI_NUM_REGS; i++) {
Peter Zijlstrae7c02192020-03-25 14:04:45 +0100224 cfi->regs[i].base = CFI_UNDEFINED;
225 cfi->vals[i].base = CFI_UNDEFINED;
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -0500226 }
Peter Zijlstrae7c02192020-03-25 14:04:45 +0100227 cfi->cfa.base = CFI_UNDEFINED;
228 cfi->drap_reg = CFI_UNDEFINED;
229 cfi->drap_offset = -1;
230}
231
Peter Zijlstra932f8e92020-03-23 18:26:03 +0100232static void init_insn_state(struct insn_state *state, struct section *sec)
Peter Zijlstrae7c02192020-03-25 14:04:45 +0100233{
234 memset(state, 0, sizeof(*state));
235 init_cfi_state(&state->cfi);
Peter Zijlstra932f8e92020-03-23 18:26:03 +0100236
237 /*
238 * We need the full vmlinux for noinstr validation, otherwise we can
239 * not correctly determine insn->call_dest->sec (external symbols do
240 * not have a section).
241 */
242 if (vmlinux && sec)
243 state->noinstr = sec->noinstr;
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500244}
245
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500246/*
247 * Call the arch-specific instruction decoder for all the instructions and add
248 * them to the global instruction list.
249 */
250static int decode_instructions(struct objtool_file *file)
251{
252 struct section *sec;
253 struct symbol *func;
254 unsigned long offset;
255 struct instruction *insn;
Peter Zijlstra1e11f3f2020-03-12 09:26:29 +0100256 unsigned long nr_insns = 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500257 int ret;
258
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500259 for_each_sec(file, sec) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500260
261 if (!(sec->sh.sh_flags & SHF_EXECINSTR))
262 continue;
263
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500264 if (strcmp(sec->name, ".altinstr_replacement") &&
265 strcmp(sec->name, ".altinstr_aux") &&
266 strncmp(sec->name, ".discard.", 9))
267 sec->text = true;
268
Thomas Gleixner0cc9ac8d2020-03-25 17:18:17 +0100269 if (!strcmp(sec->name, ".noinstr.text") ||
270 !strcmp(sec->name, ".entry.text"))
Peter Zijlstrac4a33932020-03-10 18:57:41 +0100271 sec->noinstr = true;
272
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500273 for (offset = 0; offset < sec->len; offset += insn->len) {
274 insn = malloc(sizeof(*insn));
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500275 if (!insn) {
276 WARN("malloc failed");
277 return -1;
278 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500279 memset(insn, 0, sizeof(*insn));
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500280 INIT_LIST_HEAD(&insn->alts);
Julien Thierry65ea47d2020-03-27 15:28:47 +0000281 INIT_LIST_HEAD(&insn->stack_ops);
Peter Zijlstrae7c02192020-03-25 14:04:45 +0100282 init_cfi_state(&insn->cfi);
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500283
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500284 insn->sec = sec;
285 insn->offset = offset;
286
287 ret = arch_decode_instruction(file->elf, sec, offset,
288 sec->len - offset,
289 &insn->len, &insn->type,
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500290 &insn->immediate,
Julien Thierry65ea47d2020-03-27 15:28:47 +0000291 &insn->stack_ops);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500292 if (ret)
Kamalesh Babulalb7037982017-10-19 11:27:24 -0500293 goto err;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500294
Peter Zijlstra87ecb582020-03-16 15:47:27 +0100295 hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset));
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500296 list_add_tail(&insn->list, &file->insn_list);
Peter Zijlstra1e11f3f2020-03-12 09:26:29 +0100297 nr_insns++;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500298 }
299
300 list_for_each_entry(func, &sec->symbol_list, list) {
Josh Poimboeufe10cd8f2019-07-17 20:36:48 -0500301 if (func->type != STT_FUNC || func->alias != func)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500302 continue;
303
304 if (!find_insn(file, sec, func->offset)) {
305 WARN("%s(): can't find starting instruction",
306 func->name);
307 return -1;
308 }
309
Peter Zijlstradbf4aeb2020-03-10 18:24:59 +0100310 sym_for_each_insn(file, func, insn)
Josh Poimboeufe10cd8f2019-07-17 20:36:48 -0500311 insn->func = func;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500312 }
313 }
314
Peter Zijlstra1e11f3f2020-03-12 09:26:29 +0100315 if (stats)
316 printf("nr_insns: %lu\n", nr_insns);
317
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500318 return 0;
Kamalesh Babulalb7037982017-10-19 11:27:24 -0500319
320err:
321 free(insn);
322 return ret;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500323}
324
325/*
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500326 * Mark "ud2" instructions and manually annotated dead ends.
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500327 */
328static int add_dead_ends(struct objtool_file *file)
329{
330 struct section *sec;
331 struct rela *rela;
332 struct instruction *insn;
333 bool found;
334
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500335 /*
336 * By default, "ud2" is a dead end unless otherwise annotated, because
337 * GCC 7 inserts it for certain divide-by-zero cases.
338 */
339 for_each_insn(file, insn)
340 if (insn->type == INSN_BUG)
341 insn->dead_end = true;
342
343 /*
344 * Check for manually annotated dead ends.
345 */
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500346 sec = find_section_by_name(file->elf, ".rela.discard.unreachable");
347 if (!sec)
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500348 goto reachable;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500349
350 list_for_each_entry(rela, &sec->rela_list, list) {
351 if (rela->sym->type != STT_SECTION) {
352 WARN("unexpected relocation symbol type in %s", sec->name);
353 return -1;
354 }
355 insn = find_insn(file, rela->sym->sec, rela->addend);
356 if (insn)
357 insn = list_prev_entry(insn, list);
358 else if (rela->addend == rela->sym->sec->len) {
359 found = false;
360 list_for_each_entry_reverse(insn, &file->insn_list, list) {
361 if (insn->sec == rela->sym->sec) {
362 found = true;
363 break;
364 }
365 }
366
367 if (!found) {
368 WARN("can't find unreachable insn at %s+0x%x",
369 rela->sym->sec->name, rela->addend);
370 return -1;
371 }
372 } else {
373 WARN("can't find unreachable insn at %s+0x%x",
374 rela->sym->sec->name, rela->addend);
375 return -1;
376 }
377
378 insn->dead_end = true;
379 }
380
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500381reachable:
382 /*
383 * These manually annotated reachable checks are needed for GCC 4.4,
384 * where the Linux unreachable() macro isn't supported. In that case
385 * GCC doesn't know the "ud2" is fatal, so it generates code as if it's
386 * not a dead end.
387 */
388 sec = find_section_by_name(file->elf, ".rela.discard.reachable");
389 if (!sec)
390 return 0;
391
392 list_for_each_entry(rela, &sec->rela_list, list) {
393 if (rela->sym->type != STT_SECTION) {
394 WARN("unexpected relocation symbol type in %s", sec->name);
395 return -1;
396 }
397 insn = find_insn(file, rela->sym->sec, rela->addend);
398 if (insn)
399 insn = list_prev_entry(insn, list);
400 else if (rela->addend == rela->sym->sec->len) {
401 found = false;
402 list_for_each_entry_reverse(insn, &file->insn_list, list) {
403 if (insn->sec == rela->sym->sec) {
404 found = true;
405 break;
406 }
407 }
408
409 if (!found) {
410 WARN("can't find reachable insn at %s+0x%x",
411 rela->sym->sec->name, rela->addend);
412 return -1;
413 }
414 } else {
415 WARN("can't find reachable insn at %s+0x%x",
416 rela->sym->sec->name, rela->addend);
417 return -1;
418 }
419
420 insn->dead_end = false;
421 }
422
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500423 return 0;
424}
425
426/*
427 * Warnings shouldn't be reported for ignored functions.
428 */
429static void add_ignores(struct objtool_file *file)
430{
431 struct instruction *insn;
432 struct section *sec;
433 struct symbol *func;
Peter Zijlstraaaf5c622019-02-27 14:04:13 +0100434 struct rela *rela;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500435
Peter Zijlstraaaf5c622019-02-27 14:04:13 +0100436 sec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard");
437 if (!sec)
438 return;
439
440 list_for_each_entry(rela, &sec->rela_list, list) {
441 switch (rela->sym->type) {
442 case STT_FUNC:
443 func = rela->sym;
444 break;
445
446 case STT_SECTION:
Josh Poimboeuf7acfe532020-02-17 21:41:54 -0600447 func = find_func_by_offset(rela->sym->sec, rela->addend);
448 if (!func)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500449 continue;
Peter Zijlstraaaf5c622019-02-27 14:04:13 +0100450 break;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500451
Peter Zijlstraaaf5c622019-02-27 14:04:13 +0100452 default:
453 WARN("unexpected relocation symbol type in %s: %d", sec->name, rela->sym->type);
454 continue;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500455 }
Peter Zijlstraaaf5c622019-02-27 14:04:13 +0100456
Peter Zijlstraf0f70ad2020-03-10 18:27:24 +0100457 func_for_each_insn(file, func, insn)
Peter Zijlstraaaf5c622019-02-27 14:04:13 +0100458 insn->ignore = true;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500459 }
460}
461
462/*
Peter Zijlstraea242132019-02-25 12:50:09 +0100463 * This is a whitelist of functions that is allowed to be called with AC set.
464 * The list is meant to be minimal and only contains compiler instrumentation
465 * ABI and a few functions used to implement *_{to,from}_user() functions.
466 *
467 * These functions must not directly change AC, but may PUSHF/POPF.
468 */
469static const char *uaccess_safe_builtin[] = {
470 /* KASAN */
471 "kasan_report",
472 "check_memory_region",
473 /* KASAN out-of-line */
474 "__asan_loadN_noabort",
475 "__asan_load1_noabort",
476 "__asan_load2_noabort",
477 "__asan_load4_noabort",
478 "__asan_load8_noabort",
479 "__asan_load16_noabort",
480 "__asan_storeN_noabort",
481 "__asan_store1_noabort",
482 "__asan_store2_noabort",
483 "__asan_store4_noabort",
484 "__asan_store8_noabort",
485 "__asan_store16_noabort",
486 /* KASAN in-line */
487 "__asan_report_load_n_noabort",
488 "__asan_report_load1_noabort",
489 "__asan_report_load2_noabort",
490 "__asan_report_load4_noabort",
491 "__asan_report_load8_noabort",
492 "__asan_report_load16_noabort",
493 "__asan_report_store_n_noabort",
494 "__asan_report_store1_noabort",
495 "__asan_report_store2_noabort",
496 "__asan_report_store4_noabort",
497 "__asan_report_store8_noabort",
498 "__asan_report_store16_noabort",
499 /* KCOV */
500 "write_comp_data",
501 "__sanitizer_cov_trace_pc",
502 "__sanitizer_cov_trace_const_cmp1",
503 "__sanitizer_cov_trace_const_cmp2",
504 "__sanitizer_cov_trace_const_cmp4",
505 "__sanitizer_cov_trace_const_cmp8",
506 "__sanitizer_cov_trace_cmp1",
507 "__sanitizer_cov_trace_cmp2",
508 "__sanitizer_cov_trace_cmp4",
509 "__sanitizer_cov_trace_cmp8",
Al Viro36b1c702020-02-16 13:07:49 -0500510 "__sanitizer_cov_trace_switch",
Peter Zijlstraea242132019-02-25 12:50:09 +0100511 /* UBSAN */
512 "ubsan_type_mismatch_common",
513 "__ubsan_handle_type_mismatch",
514 "__ubsan_handle_type_mismatch_v1",
Peter Zijlstra9a50dca2019-10-21 15:11:49 +0200515 "__ubsan_handle_shift_out_of_bounds",
Peter Zijlstraea242132019-02-25 12:50:09 +0100516 /* misc */
517 "csum_partial_copy_generic",
518 "__memcpy_mcsafe",
Josh Poimboeufa7e47f22019-07-17 20:36:46 -0500519 "mcsafe_handle_tail",
Peter Zijlstraea242132019-02-25 12:50:09 +0100520 "ftrace_likely_update", /* CONFIG_TRACE_BRANCH_PROFILING */
521 NULL
522};
523
524static void add_uaccess_safe(struct objtool_file *file)
525{
526 struct symbol *func;
527 const char **name;
528
529 if (!uaccess)
530 return;
531
532 for (name = uaccess_safe_builtin; *name; name++) {
533 func = find_symbol_by_name(file->elf, *name);
534 if (!func)
535 continue;
536
Josh Poimboeufe10cd8f2019-07-17 20:36:48 -0500537 func->uaccess_safe = true;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500538 }
539}
540
541/*
Josh Poimboeuf258c7602018-01-11 21:46:24 +0000542 * FIXME: For now, just ignore any alternatives which add retpolines. This is
543 * a temporary hack, as it doesn't allow ORC to unwind from inside a retpoline.
544 * But it at least allows objtool to understand the control flow *around* the
545 * retpoline.
546 */
Peter Zijlstraff05ab22019-03-18 14:33:07 +0100547static int add_ignore_alternatives(struct objtool_file *file)
Josh Poimboeuf258c7602018-01-11 21:46:24 +0000548{
549 struct section *sec;
550 struct rela *rela;
551 struct instruction *insn;
552
Peter Zijlstraff05ab22019-03-18 14:33:07 +0100553 sec = find_section_by_name(file->elf, ".rela.discard.ignore_alts");
Josh Poimboeuf258c7602018-01-11 21:46:24 +0000554 if (!sec)
555 return 0;
556
557 list_for_each_entry(rela, &sec->rela_list, list) {
558 if (rela->sym->type != STT_SECTION) {
559 WARN("unexpected relocation symbol type in %s", sec->name);
560 return -1;
561 }
562
563 insn = find_insn(file, rela->sym->sec, rela->addend);
564 if (!insn) {
Peter Zijlstraff05ab22019-03-18 14:33:07 +0100565 WARN("bad .discard.ignore_alts entry");
Josh Poimboeuf258c7602018-01-11 21:46:24 +0000566 return -1;
567 }
568
569 insn->ignore_alts = true;
570 }
571
572 return 0;
573}
574
575/*
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500576 * Find the destination instructions for all jumps.
577 */
578static int add_jump_destinations(struct objtool_file *file)
579{
580 struct instruction *insn;
581 struct rela *rela;
582 struct section *dest_sec;
583 unsigned long dest_off;
584
585 for_each_insn(file, insn) {
Josh Poimboeufa2296142020-02-10 12:32:39 -0600586 if (!is_static_jump(insn))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500587 continue;
588
Josh Poimboeufe6da9562019-05-13 12:01:31 -0500589 if (insn->ignore || insn->offset == FAKE_JUMP_OFFSET)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500590 continue;
591
Peter Zijlstra8b5fa6b2020-03-12 11:23:36 +0100592 rela = find_rela_by_dest_range(file->elf, insn->sec,
593 insn->offset, insn->len);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500594 if (!rela) {
595 dest_sec = insn->sec;
Raphael Gaultbfb08f22020-03-27 15:28:45 +0000596 dest_off = arch_jump_destination(insn);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500597 } else if (rela->sym->type == STT_SECTION) {
598 dest_sec = rela->sym->sec;
Raphael Gaultbfb08f22020-03-27 15:28:45 +0000599 dest_off = arch_dest_rela_offset(rela->addend);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500600 } else if (rela->sym->sec->idx) {
601 dest_sec = rela->sym->sec;
Raphael Gaultbfb08f22020-03-27 15:28:45 +0000602 dest_off = rela->sym->sym.st_value +
603 arch_dest_rela_offset(rela->addend);
Josh Poimboeuf39b73532018-01-11 21:46:23 +0000604 } else if (strstr(rela->sym->name, "_indirect_thunk_")) {
605 /*
606 * Retpoline jumps are really dynamic jumps in
607 * disguise, so convert them accordingly.
608 */
Josh Poimboeufb68b9902019-07-17 20:36:57 -0500609 if (insn->type == INSN_JUMP_UNCONDITIONAL)
610 insn->type = INSN_JUMP_DYNAMIC;
611 else
612 insn->type = INSN_JUMP_DYNAMIC_CONDITIONAL;
613
Peter Zijlstrab5bc2232018-01-16 10:24:06 +0100614 insn->retpoline_safe = true;
Josh Poimboeuf39b73532018-01-11 21:46:23 +0000615 continue;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500616 } else {
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -0500617 /* external sibling call */
Peter Zijlstra54262aa2019-03-06 12:58:15 +0100618 insn->call_dest = rela->sym;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500619 continue;
620 }
621
622 insn->jump_dest = find_insn(file, dest_sec, dest_off);
623 if (!insn->jump_dest) {
624
625 /*
626 * This is a special case where an alt instruction
627 * jumps past the end of the section. These are
628 * handled later in handle_group_alt().
629 */
630 if (!strcmp(insn->sec->name, ".altinstr_replacement"))
631 continue;
632
633 WARN_FUNC("can't find jump dest instruction at %s+0x%lx",
634 insn->sec, insn->offset, dest_sec->name,
635 dest_off);
636 return -1;
637 }
Josh Poimboeufcd778492018-06-01 07:23:51 -0500638
639 /*
Peter Zijlstra54262aa2019-03-06 12:58:15 +0100640 * Cross-function jump.
Josh Poimboeufcd778492018-06-01 07:23:51 -0500641 */
642 if (insn->func && insn->jump_dest->func &&
Peter Zijlstra54262aa2019-03-06 12:58:15 +0100643 insn->func != insn->jump_dest->func) {
644
645 /*
646 * For GCC 8+, create parent/child links for any cold
647 * subfunctions. This is _mostly_ redundant with a
648 * similar initialization in read_symbols().
649 *
650 * If a function has aliases, we want the *first* such
651 * function in the symbol table to be the subfunction's
652 * parent. In that case we overwrite the
653 * initialization done in read_symbols().
654 *
655 * However this code can't completely replace the
656 * read_symbols() code because this doesn't detect the
657 * case where the parent function's only reference to a
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -0500658 * subfunction is through a jump table.
Peter Zijlstra54262aa2019-03-06 12:58:15 +0100659 */
660 if (!strstr(insn->func->name, ".cold.") &&
661 strstr(insn->jump_dest->func->name, ".cold.")) {
662 insn->func->cfunc = insn->jump_dest->func;
663 insn->jump_dest->func->pfunc = insn->func;
664
665 } else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
666 insn->jump_dest->offset == insn->jump_dest->func->offset) {
667
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -0500668 /* internal sibling call */
Peter Zijlstra54262aa2019-03-06 12:58:15 +0100669 insn->call_dest = insn->jump_dest->func;
Peter Zijlstra54262aa2019-03-06 12:58:15 +0100670 }
Josh Poimboeufcd778492018-06-01 07:23:51 -0500671 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500672 }
673
674 return 0;
675}
676
677/*
678 * Find the destination instructions for all calls.
679 */
680static int add_call_destinations(struct objtool_file *file)
681{
682 struct instruction *insn;
683 unsigned long dest_off;
684 struct rela *rela;
685
686 for_each_insn(file, insn) {
687 if (insn->type != INSN_CALL)
688 continue;
689
Peter Zijlstra8b5fa6b2020-03-12 11:23:36 +0100690 rela = find_rela_by_dest_range(file->elf, insn->sec,
691 insn->offset, insn->len);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500692 if (!rela) {
Raphael Gaultbfb08f22020-03-27 15:28:45 +0000693 dest_off = arch_jump_destination(insn);
Josh Poimboeuf7acfe532020-02-17 21:41:54 -0600694 insn->call_dest = find_func_by_offset(insn->sec, dest_off);
695 if (!insn->call_dest)
696 insn->call_dest = find_symbol_by_offset(insn->sec, dest_off);
Josh Poimboeufa845c7c2018-01-29 22:00:39 -0600697
Josh Poimboeuf7acfe532020-02-17 21:41:54 -0600698 if (insn->ignore)
699 continue;
700
701 if (!insn->call_dest) {
Josh Poimboeufa845c7c2018-01-29 22:00:39 -0600702 WARN_FUNC("unsupported intra-function call",
703 insn->sec, insn->offset);
Peter Zijlstrab5bc2232018-01-16 10:24:06 +0100704 if (retpoline)
705 WARN("If this is a retpoline, please patch it in with alternatives and annotate it with ANNOTATE_NOSPEC_ALTERNATIVE.");
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500706 return -1;
707 }
Josh Poimboeufa845c7c2018-01-29 22:00:39 -0600708
Josh Poimboeuf7acfe532020-02-17 21:41:54 -0600709 if (insn->func && insn->call_dest->type != STT_FUNC) {
710 WARN_FUNC("unsupported call to non-function",
711 insn->sec, insn->offset);
712 return -1;
713 }
714
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500715 } else if (rela->sym->type == STT_SECTION) {
Raphael Gaultbfb08f22020-03-27 15:28:45 +0000716 dest_off = arch_dest_rela_offset(rela->addend);
Josh Poimboeuf7acfe532020-02-17 21:41:54 -0600717 insn->call_dest = find_func_by_offset(rela->sym->sec,
Raphael Gaultbfb08f22020-03-27 15:28:45 +0000718 dest_off);
Josh Poimboeuf7acfe532020-02-17 21:41:54 -0600719 if (!insn->call_dest) {
Raphael Gaultbfb08f22020-03-27 15:28:45 +0000720 WARN_FUNC("can't find call dest symbol at %s+0x%lx",
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500721 insn->sec, insn->offset,
722 rela->sym->sec->name,
Raphael Gaultbfb08f22020-03-27 15:28:45 +0000723 dest_off);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500724 return -1;
725 }
726 } else
727 insn->call_dest = rela->sym;
728 }
729
730 return 0;
731}
732
733/*
734 * The .alternatives section requires some extra special care, over and above
735 * what other special sections require:
736 *
737 * 1. Because alternatives are patched in-place, we need to insert a fake jump
738 * instruction at the end so that validate_branch() skips all the original
739 * replaced instructions when validating the new instruction path.
740 *
741 * 2. An added wrinkle is that the new instruction length might be zero. In
742 * that case the old instructions are replaced with noops. We simulate that
743 * by creating a fake jump as the only new instruction.
744 *
745 * 3. In some cases, the alternative section includes an instruction which
746 * conditionally jumps to the _end_ of the entry. We have to modify these
747 * jumps' destinations to point back to .text rather than the end of the
748 * entry in .altinstr_replacement.
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500749 */
750static int handle_group_alt(struct objtool_file *file,
751 struct special_alt *special_alt,
752 struct instruction *orig_insn,
753 struct instruction **new_insn)
754{
Josh Poimboeuf17bc3392018-01-29 22:00:40 -0600755 struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump = NULL;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500756 unsigned long dest_off;
757
758 last_orig_insn = NULL;
759 insn = orig_insn;
760 sec_for_each_insn_from(file, insn) {
761 if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
762 break;
763
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500764 insn->alt_group = true;
765 last_orig_insn = insn;
766 }
767
Josh Poimboeuf17bc3392018-01-29 22:00:40 -0600768 if (next_insn_same_sec(file, last_orig_insn)) {
769 fake_jump = malloc(sizeof(*fake_jump));
770 if (!fake_jump) {
771 WARN("malloc failed");
772 return -1;
773 }
774 memset(fake_jump, 0, sizeof(*fake_jump));
775 INIT_LIST_HEAD(&fake_jump->alts);
Julien Thierry65ea47d2020-03-27 15:28:47 +0000776 INIT_LIST_HEAD(&fake_jump->stack_ops);
Peter Zijlstrae7c02192020-03-25 14:04:45 +0100777 init_cfi_state(&fake_jump->cfi);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500778
Josh Poimboeuf17bc3392018-01-29 22:00:40 -0600779 fake_jump->sec = special_alt->new_sec;
Josh Poimboeufe6da9562019-05-13 12:01:31 -0500780 fake_jump->offset = FAKE_JUMP_OFFSET;
Josh Poimboeuf17bc3392018-01-29 22:00:40 -0600781 fake_jump->type = INSN_JUMP_UNCONDITIONAL;
782 fake_jump->jump_dest = list_next_entry(last_orig_insn, list);
Josh Poimboeufe6da9562019-05-13 12:01:31 -0500783 fake_jump->func = orig_insn->func;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500784 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500785
786 if (!special_alt->new_len) {
Josh Poimboeuf17bc3392018-01-29 22:00:40 -0600787 if (!fake_jump) {
788 WARN("%s: empty alternative at end of section",
789 special_alt->orig_sec->name);
790 return -1;
791 }
792
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500793 *new_insn = fake_jump;
794 return 0;
795 }
796
797 last_new_insn = NULL;
798 insn = *new_insn;
799 sec_for_each_insn_from(file, insn) {
800 if (insn->offset >= special_alt->new_off + special_alt->new_len)
801 break;
802
803 last_new_insn = insn;
804
Josh Poimboeufa845c7c2018-01-29 22:00:39 -0600805 insn->ignore = orig_insn->ignore_alts;
Peter Zijlstraa4d09dd2019-02-25 10:31:24 +0100806 insn->func = orig_insn->func;
Josh Poimboeufa845c7c2018-01-29 22:00:39 -0600807
Josh Poimboeufdc419722020-02-10 12:32:40 -0600808 /*
809 * Since alternative replacement code is copy/pasted by the
810 * kernel after applying relocations, generally such code can't
811 * have relative-address relocation references to outside the
812 * .altinstr_replacement section, unless the arch's
813 * alternatives code can adjust the relative offsets
814 * accordingly.
815 *
816 * The x86 alternatives code adjusts the offsets only when it
817 * encounters a branch instruction at the very beginning of the
818 * replacement group.
819 */
820 if ((insn->offset != special_alt->new_off ||
821 (insn->type != INSN_CALL && !is_static_jump(insn))) &&
Peter Zijlstra8b5fa6b2020-03-12 11:23:36 +0100822 find_rela_by_dest_range(file->elf, insn->sec, insn->offset, insn->len)) {
Josh Poimboeufdc419722020-02-10 12:32:40 -0600823
824 WARN_FUNC("unsupported relocation in alternatives section",
825 insn->sec, insn->offset);
826 return -1;
827 }
828
Josh Poimboeufa2296142020-02-10 12:32:39 -0600829 if (!is_static_jump(insn))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500830 continue;
831
832 if (!insn->immediate)
833 continue;
834
Raphael Gaultbfb08f22020-03-27 15:28:45 +0000835 dest_off = arch_jump_destination(insn);
Josh Poimboeuf17bc3392018-01-29 22:00:40 -0600836 if (dest_off == special_alt->new_off + special_alt->new_len) {
837 if (!fake_jump) {
838 WARN("%s: alternative jump to end of section",
839 special_alt->orig_sec->name);
840 return -1;
841 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500842 insn->jump_dest = fake_jump;
Josh Poimboeuf17bc3392018-01-29 22:00:40 -0600843 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500844
845 if (!insn->jump_dest) {
846 WARN_FUNC("can't find alternative jump destination",
847 insn->sec, insn->offset);
848 return -1;
849 }
850 }
851
852 if (!last_new_insn) {
853 WARN_FUNC("can't find last new alternative instruction",
854 special_alt->new_sec, special_alt->new_off);
855 return -1;
856 }
857
Josh Poimboeuf17bc3392018-01-29 22:00:40 -0600858 if (fake_jump)
859 list_add(&fake_jump->list, &last_new_insn->list);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500860
861 return 0;
862}
863
864/*
865 * A jump table entry can either convert a nop to a jump or a jump to a nop.
866 * If the original instruction is a jump, make the alt entry an effective nop
867 * by just skipping the original instruction.
868 */
869static int handle_jump_alt(struct objtool_file *file,
870 struct special_alt *special_alt,
871 struct instruction *orig_insn,
872 struct instruction **new_insn)
873{
874 if (orig_insn->type == INSN_NOP)
875 return 0;
876
877 if (orig_insn->type != INSN_JUMP_UNCONDITIONAL) {
878 WARN_FUNC("unsupported instruction at jump label",
879 orig_insn->sec, orig_insn->offset);
880 return -1;
881 }
882
883 *new_insn = list_next_entry(orig_insn, list);
884 return 0;
885}
886
887/*
888 * Read all the special sections which have alternate instructions which can be
889 * patched in or redirected to at runtime. Each instruction having alternate
890 * instruction(s) has them added to its insn->alts list, which will be
891 * traversed in validate_branch().
892 */
893static int add_special_section_alts(struct objtool_file *file)
894{
895 struct list_head special_alts;
896 struct instruction *orig_insn, *new_insn;
897 struct special_alt *special_alt, *tmp;
898 struct alternative *alt;
899 int ret;
900
901 ret = special_get_alts(file->elf, &special_alts);
902 if (ret)
903 return ret;
904
905 list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500906
907 orig_insn = find_insn(file, special_alt->orig_sec,
908 special_alt->orig_off);
909 if (!orig_insn) {
910 WARN_FUNC("special: can't find orig instruction",
911 special_alt->orig_sec, special_alt->orig_off);
912 ret = -1;
913 goto out;
914 }
915
916 new_insn = NULL;
917 if (!special_alt->group || special_alt->new_len) {
918 new_insn = find_insn(file, special_alt->new_sec,
919 special_alt->new_off);
920 if (!new_insn) {
921 WARN_FUNC("special: can't find new instruction",
922 special_alt->new_sec,
923 special_alt->new_off);
924 ret = -1;
925 goto out;
926 }
927 }
928
929 if (special_alt->group) {
Julien Thierry7170cf42020-03-27 15:28:41 +0000930 if (!special_alt->orig_len) {
931 WARN_FUNC("empty alternative entry",
932 orig_insn->sec, orig_insn->offset);
933 continue;
934 }
935
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500936 ret = handle_group_alt(file, special_alt, orig_insn,
937 &new_insn);
938 if (ret)
939 goto out;
940 } else if (special_alt->jump_or_nop) {
941 ret = handle_jump_alt(file, special_alt, orig_insn,
942 &new_insn);
943 if (ret)
944 goto out;
945 }
946
Josh Poimboeuf258c7602018-01-11 21:46:24 +0000947 alt = malloc(sizeof(*alt));
948 if (!alt) {
949 WARN("malloc failed");
950 ret = -1;
951 goto out;
952 }
953
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500954 alt->insn = new_insn;
Peter Zijlstra764eef42019-03-01 11:19:03 +0100955 alt->skip_orig = special_alt->skip_orig;
Peter Zijlstraea242132019-02-25 12:50:09 +0100956 orig_insn->ignore_alts |= special_alt->skip_alt;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500957 list_add_tail(&alt->list, &orig_insn->alts);
958
959 list_del(&special_alt->list);
960 free(special_alt);
961 }
962
963out:
964 return ret;
965}
966
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -0500967static int add_jump_table(struct objtool_file *file, struct instruction *insn,
Jann Hornbd98c812019-07-17 20:36:54 -0500968 struct rela *table)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500969{
970 struct rela *rela = table;
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -0500971 struct instruction *dest_insn;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500972 struct alternative *alt;
Josh Poimboeuffd35c882018-05-10 17:48:49 -0500973 struct symbol *pfunc = insn->func->pfunc;
974 unsigned int prev_offset = 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500975
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -0500976 /*
977 * Each @rela is a switch table relocation which points to the target
978 * instruction.
979 */
980 list_for_each_entry_from(rela, &table->sec->rela_list, list) {
Jann Hornbd98c812019-07-17 20:36:54 -0500981
982 /* Check for the end of the table: */
983 if (rela != table && rela->jump_table_start)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500984 break;
985
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -0500986 /* Make sure the table entries are consecutive: */
Josh Poimboeuffd35c882018-05-10 17:48:49 -0500987 if (prev_offset && rela->offset != prev_offset + 8)
988 break;
989
990 /* Detect function pointers from contiguous objects: */
991 if (rela->sym->sec == pfunc->sec &&
992 rela->addend == pfunc->offset)
993 break;
994
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -0500995 dest_insn = find_insn(file, rela->sym->sec, rela->addend);
996 if (!dest_insn)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500997 break;
998
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -0500999 /* Make sure the destination is in the same function: */
Josh Poimboeufe65050b2019-07-17 20:36:55 -05001000 if (!dest_insn->func || dest_insn->func->pfunc != pfunc)
Josh Poimboeuf13810432018-05-09 22:39:15 -05001001 break;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001002
1003 alt = malloc(sizeof(*alt));
1004 if (!alt) {
1005 WARN("malloc failed");
1006 return -1;
1007 }
1008
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001009 alt->insn = dest_insn;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001010 list_add_tail(&alt->list, &insn->alts);
Josh Poimboeuffd35c882018-05-10 17:48:49 -05001011 prev_offset = rela->offset;
1012 }
1013
1014 if (!prev_offset) {
1015 WARN_FUNC("can't find switch jump table",
1016 insn->sec, insn->offset);
1017 return -1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001018 }
1019
1020 return 0;
1021}
1022
1023/*
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001024 * find_jump_table() - Given a dynamic jump, find the switch jump table in
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001025 * .rodata associated with it.
1026 *
1027 * There are 3 basic patterns:
1028 *
1029 * 1. jmpq *[rodata addr](,%reg,8)
1030 *
1031 * This is the most common case by far. It jumps to an address in a simple
1032 * jump table which is stored in .rodata.
1033 *
1034 * 2. jmpq *[rodata addr](%rip)
1035 *
1036 * This is caused by a rare GCC quirk, currently only seen in three driver
1037 * functions in the kernel, only with certain obscure non-distro configs.
1038 *
1039 * As part of an optimization, GCC makes a copy of an existing switch jump
1040 * table, modifies it, and then hard-codes the jump (albeit with an indirect
1041 * jump) to use a single entry in the table. The rest of the jump table and
1042 * some of its jump targets remain as dead code.
1043 *
1044 * In such a case we can just crudely ignore all unreachable instruction
1045 * warnings for the entire object file. Ideally we would just ignore them
1046 * for the function, but that would require redesigning the code quite a
1047 * bit. And honestly that's just not worth doing: unreachable instruction
1048 * warnings are of questionable value anyway, and this is such a rare issue.
1049 *
1050 * 3. mov [rodata addr],%reg1
1051 * ... some instructions ...
1052 * jmpq *(%reg1,%reg2,8)
1053 *
1054 * This is a fairly uncommon pattern which is new for GCC 6. As of this
1055 * writing, there are 11 occurrences of it in the allmodconfig kernel.
1056 *
Peter Zijlstra99ce7962018-02-08 14:02:32 +01001057 * As of GCC 7 there are quite a few more of these and the 'in between' code
1058 * is significant. Esp. with KASAN enabled some of the code between the mov
1059 * and jmpq uses .rodata itself, which can confuse things.
1060 *
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001061 * TODO: Once we have DWARF CFI and smarter instruction decoding logic,
1062 * ensure the same register is used in the mov and jump instructions.
Peter Zijlstra99ce7962018-02-08 14:02:32 +01001063 *
1064 * NOTE: RETPOLINE made it harder still to decode dynamic jumps.
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001065 */
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001066static struct rela *find_jump_table(struct objtool_file *file,
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001067 struct symbol *func,
1068 struct instruction *insn)
1069{
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001070 struct rela *text_rela, *table_rela;
Josh Poimboeuf113d4bc2020-02-17 21:41:53 -06001071 struct instruction *dest_insn, *orig_insn = insn;
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001072 struct section *table_sec;
Josh Poimboeuf6f5ec292018-05-14 08:53:24 -05001073 unsigned long table_offset;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001074
Peter Zijlstra99ce7962018-02-08 14:02:32 +01001075 /*
1076 * Backward search using the @first_jump_src links, these help avoid
1077 * much of the 'in between' code. Which avoids us getting confused by
1078 * it.
1079 */
Josh Poimboeuf7dec80c2018-05-18 15:10:34 -05001080 for (;
Josh Poimboeufb401efc2020-04-01 13:23:28 -05001081 &insn->list != &file->insn_list && insn->func && insn->func->pfunc == func;
Peter Zijlstra99ce7962018-02-08 14:02:32 +01001082 insn = insn->first_jump_src ?: list_prev_entry(insn, list)) {
1083
Josh Poimboeuf7dec80c2018-05-18 15:10:34 -05001084 if (insn != orig_insn && insn->type == INSN_JUMP_DYNAMIC)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001085 break;
1086
1087 /* allow small jumps within the range */
1088 if (insn->type == INSN_JUMP_UNCONDITIONAL &&
1089 insn->jump_dest &&
1090 (insn->jump_dest->offset <= insn->offset ||
1091 insn->jump_dest->offset > orig_insn->offset))
1092 break;
1093
1094 /* look for a relocation which references .rodata */
Peter Zijlstra8b5fa6b2020-03-12 11:23:36 +01001095 text_rela = find_rela_by_dest_range(file->elf, insn->sec,
1096 insn->offset, insn->len);
Allan Xavier4a60aa02018-09-07 08:12:01 -05001097 if (!text_rela || text_rela->sym->type != STT_SECTION ||
1098 !text_rela->sym->sec->rodata)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001099 continue;
1100
Josh Poimboeuf6f5ec292018-05-14 08:53:24 -05001101 table_offset = text_rela->addend;
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001102 table_sec = text_rela->sym->sec;
Allan Xavier4a60aa02018-09-07 08:12:01 -05001103
Josh Poimboeuf6f5ec292018-05-14 08:53:24 -05001104 if (text_rela->type == R_X86_64_PC32)
1105 table_offset += 4;
1106
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001107 /*
1108 * Make sure the .rodata address isn't associated with a
Josh Poimboeuf87b512d2019-06-27 20:50:46 -05001109 * symbol. GCC jump tables are anonymous data.
1110 *
1111 * Also support C jump tables which are in the same format as
1112 * switch jump tables. For objtool to recognize them, they
1113 * need to be placed in the C_JUMP_TABLE_SECTION section. They
1114 * have symbols associated with them.
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001115 */
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001116 if (find_symbol_containing(table_sec, table_offset) &&
1117 strcmp(table_sec->name, C_JUMP_TABLE_SECTION))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001118 continue;
1119
Josh Poimboeuf113d4bc2020-02-17 21:41:53 -06001120 /*
1121 * Each table entry has a rela associated with it. The rela
1122 * should reference text in the same function as the original
1123 * instruction.
1124 */
Peter Zijlstra8b5fa6b2020-03-12 11:23:36 +01001125 table_rela = find_rela_by_dest(file->elf, table_sec, table_offset);
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001126 if (!table_rela)
1127 continue;
Josh Poimboeuf113d4bc2020-02-17 21:41:53 -06001128 dest_insn = find_insn(file, table_rela->sym->sec, table_rela->addend);
1129 if (!dest_insn || !dest_insn->func || dest_insn->func->pfunc != func)
1130 continue;
Josh Poimboeuf7dec80c2018-05-18 15:10:34 -05001131
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001132 /*
1133 * Use of RIP-relative switch jumps is quite rare, and
1134 * indicates a rare GCC quirk/bug which can leave dead code
1135 * behind.
1136 */
1137 if (text_rela->type == R_X86_64_PC32)
1138 file->ignore_unreachables = true;
1139
1140 return table_rela;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001141 }
1142
1143 return NULL;
1144}
1145
Jann Hornbd98c812019-07-17 20:36:54 -05001146/*
1147 * First pass: Mark the head of each jump table so that in the next pass,
1148 * we know when a given jump table ends and the next one starts.
1149 */
1150static void mark_func_jump_tables(struct objtool_file *file,
1151 struct symbol *func)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001152{
Jann Hornbd98c812019-07-17 20:36:54 -05001153 struct instruction *insn, *last = NULL;
1154 struct rela *rela;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001155
Peter Zijlstraf0f70ad2020-03-10 18:27:24 +01001156 func_for_each_insn(file, func, insn) {
Peter Zijlstra99ce7962018-02-08 14:02:32 +01001157 if (!last)
1158 last = insn;
1159
1160 /*
1161 * Store back-pointers for unconditional forward jumps such
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001162 * that find_jump_table() can back-track using those and
Peter Zijlstra99ce7962018-02-08 14:02:32 +01001163 * avoid some potentially confusing code.
1164 */
1165 if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest &&
1166 insn->offset > last->offset &&
1167 insn->jump_dest->offset > insn->offset &&
1168 !insn->jump_dest->first_jump_src) {
1169
1170 insn->jump_dest->first_jump_src = insn;
1171 last = insn->jump_dest;
1172 }
1173
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001174 if (insn->type != INSN_JUMP_DYNAMIC)
1175 continue;
1176
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001177 rela = find_jump_table(file, func, insn);
Jann Hornbd98c812019-07-17 20:36:54 -05001178 if (rela) {
1179 rela->jump_table_start = true;
1180 insn->jump_table = rela;
1181 }
1182 }
1183}
1184
1185static int add_func_jump_tables(struct objtool_file *file,
1186 struct symbol *func)
1187{
1188 struct instruction *insn;
1189 int ret;
1190
Peter Zijlstraf0f70ad2020-03-10 18:27:24 +01001191 func_for_each_insn(file, func, insn) {
Jann Hornbd98c812019-07-17 20:36:54 -05001192 if (!insn->jump_table)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001193 continue;
1194
Jann Hornbd98c812019-07-17 20:36:54 -05001195 ret = add_jump_table(file, insn, insn->jump_table);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001196 if (ret)
1197 return ret;
1198 }
1199
1200 return 0;
1201}
1202
1203/*
1204 * For some switch statements, gcc generates a jump table in the .rodata
1205 * section which contains a list of addresses within the function to jump to.
1206 * This finds these jump tables and adds them to the insn->alts lists.
1207 */
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001208static int add_jump_table_alts(struct objtool_file *file)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001209{
1210 struct section *sec;
1211 struct symbol *func;
1212 int ret;
1213
Allan Xavier4a60aa02018-09-07 08:12:01 -05001214 if (!file->rodata)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001215 return 0;
1216
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001217 for_each_sec(file, sec) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001218 list_for_each_entry(func, &sec->symbol_list, list) {
1219 if (func->type != STT_FUNC)
1220 continue;
1221
Jann Hornbd98c812019-07-17 20:36:54 -05001222 mark_func_jump_tables(file, func);
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001223 ret = add_func_jump_tables(file, func);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001224 if (ret)
1225 return ret;
1226 }
1227 }
1228
1229 return 0;
1230}
1231
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001232static int read_unwind_hints(struct objtool_file *file)
1233{
1234 struct section *sec, *relasec;
1235 struct rela *rela;
1236 struct unwind_hint *hint;
1237 struct instruction *insn;
1238 struct cfi_reg *cfa;
1239 int i;
1240
1241 sec = find_section_by_name(file->elf, ".discard.unwind_hints");
1242 if (!sec)
1243 return 0;
1244
1245 relasec = sec->rela;
1246 if (!relasec) {
1247 WARN("missing .rela.discard.unwind_hints section");
1248 return -1;
1249 }
1250
1251 if (sec->len % sizeof(struct unwind_hint)) {
1252 WARN("struct unwind_hint size mismatch");
1253 return -1;
1254 }
1255
1256 file->hints = true;
1257
1258 for (i = 0; i < sec->len / sizeof(struct unwind_hint); i++) {
1259 hint = (struct unwind_hint *)sec->data->d_buf + i;
1260
Peter Zijlstra8b5fa6b2020-03-12 11:23:36 +01001261 rela = find_rela_by_dest(file->elf, sec, i * sizeof(*hint));
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001262 if (!rela) {
1263 WARN("can't find rela for unwind_hints[%d]", i);
1264 return -1;
1265 }
1266
1267 insn = find_insn(file, rela->sym->sec, rela->addend);
1268 if (!insn) {
1269 WARN("can't find insn for unwind_hints[%d]", i);
1270 return -1;
1271 }
1272
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001273 cfa = &insn->cfi.cfa;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001274
Peter Zijlstrac536ed22020-04-01 16:54:26 +02001275 if (hint->type == UNWIND_HINT_TYPE_RET_OFFSET) {
Peter Zijlstrae25eea82020-04-01 16:38:19 +02001276 insn->ret_offset = hint->sp_offset;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001277 continue;
1278 }
1279
1280 insn->hint = true;
1281
1282 switch (hint->sp_reg) {
1283 case ORC_REG_UNDEFINED:
1284 cfa->base = CFI_UNDEFINED;
1285 break;
1286 case ORC_REG_SP:
1287 cfa->base = CFI_SP;
1288 break;
1289 case ORC_REG_BP:
1290 cfa->base = CFI_BP;
1291 break;
1292 case ORC_REG_SP_INDIRECT:
1293 cfa->base = CFI_SP_INDIRECT;
1294 break;
1295 case ORC_REG_R10:
1296 cfa->base = CFI_R10;
1297 break;
1298 case ORC_REG_R13:
1299 cfa->base = CFI_R13;
1300 break;
1301 case ORC_REG_DI:
1302 cfa->base = CFI_DI;
1303 break;
1304 case ORC_REG_DX:
1305 cfa->base = CFI_DX;
1306 break;
1307 default:
1308 WARN_FUNC("unsupported unwind_hint sp base reg %d",
1309 insn->sec, insn->offset, hint->sp_reg);
1310 return -1;
1311 }
1312
1313 cfa->offset = hint->sp_offset;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001314 insn->cfi.type = hint->type;
1315 insn->cfi.end = hint->end;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001316 }
1317
1318 return 0;
1319}
1320
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001321static int read_retpoline_hints(struct objtool_file *file)
1322{
Josh Poimboeuf63474dc2018-03-06 17:58:15 -06001323 struct section *sec;
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001324 struct instruction *insn;
1325 struct rela *rela;
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001326
Josh Poimboeuf63474dc2018-03-06 17:58:15 -06001327 sec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe");
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001328 if (!sec)
1329 return 0;
1330
Josh Poimboeuf63474dc2018-03-06 17:58:15 -06001331 list_for_each_entry(rela, &sec->rela_list, list) {
1332 if (rela->sym->type != STT_SECTION) {
1333 WARN("unexpected relocation symbol type in %s", sec->name);
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001334 return -1;
1335 }
1336
1337 insn = find_insn(file, rela->sym->sec, rela->addend);
1338 if (!insn) {
Josh Poimboeuf63474dc2018-03-06 17:58:15 -06001339 WARN("bad .discard.retpoline_safe entry");
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001340 return -1;
1341 }
1342
1343 if (insn->type != INSN_JUMP_DYNAMIC &&
1344 insn->type != INSN_CALL_DYNAMIC) {
Josh Poimboeuf63474dc2018-03-06 17:58:15 -06001345 WARN_FUNC("retpoline_safe hint not an indirect jump/call",
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001346 insn->sec, insn->offset);
1347 return -1;
1348 }
1349
1350 insn->retpoline_safe = true;
1351 }
1352
1353 return 0;
1354}
1355
Peter Zijlstrac4a33932020-03-10 18:57:41 +01001356static int read_instr_hints(struct objtool_file *file)
1357{
1358 struct section *sec;
1359 struct instruction *insn;
1360 struct rela *rela;
1361
1362 sec = find_section_by_name(file->elf, ".rela.discard.instr_end");
1363 if (!sec)
1364 return 0;
1365
1366 list_for_each_entry(rela, &sec->rela_list, list) {
1367 if (rela->sym->type != STT_SECTION) {
1368 WARN("unexpected relocation symbol type in %s", sec->name);
1369 return -1;
1370 }
1371
1372 insn = find_insn(file, rela->sym->sec, rela->addend);
1373 if (!insn) {
1374 WARN("bad .discard.instr_end entry");
1375 return -1;
1376 }
1377
1378 insn->instr--;
1379 }
1380
1381 sec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
1382 if (!sec)
1383 return 0;
1384
1385 list_for_each_entry(rela, &sec->rela_list, list) {
1386 if (rela->sym->type != STT_SECTION) {
1387 WARN("unexpected relocation symbol type in %s", sec->name);
1388 return -1;
1389 }
1390
1391 insn = find_insn(file, rela->sym->sec, rela->addend);
1392 if (!insn) {
1393 WARN("bad .discard.instr_begin entry");
1394 return -1;
1395 }
1396
1397 insn->instr++;
1398 }
1399
1400 return 0;
1401}
1402
Allan Xavier4a60aa02018-09-07 08:12:01 -05001403static void mark_rodata(struct objtool_file *file)
1404{
1405 struct section *sec;
1406 bool found = false;
1407
1408 /*
Josh Poimboeuf87b512d2019-06-27 20:50:46 -05001409 * Search for the following rodata sections, each of which can
1410 * potentially contain jump tables:
1411 *
1412 * - .rodata: can contain GCC switch tables
1413 * - .rodata.<func>: same, if -fdata-sections is being used
1414 * - .rodata..c_jump_table: contains C annotated jump tables
1415 *
1416 * .rodata.str1.* sections are ignored; they don't contain jump tables.
Allan Xavier4a60aa02018-09-07 08:12:01 -05001417 */
1418 for_each_sec(file, sec) {
Muchun Song1ee444702020-04-12 22:44:05 +08001419 if (!strncmp(sec->name, ".rodata", 7) &&
1420 !strstr(sec->name, ".str1.")) {
Allan Xavier4a60aa02018-09-07 08:12:01 -05001421 sec->rodata = true;
1422 found = true;
1423 }
1424 }
1425
1426 file->rodata = found;
1427}
1428
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001429static int decode_sections(struct objtool_file *file)
1430{
1431 int ret;
1432
Allan Xavier4a60aa02018-09-07 08:12:01 -05001433 mark_rodata(file);
1434
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001435 ret = decode_instructions(file);
1436 if (ret)
1437 return ret;
1438
1439 ret = add_dead_ends(file);
1440 if (ret)
1441 return ret;
1442
1443 add_ignores(file);
Peter Zijlstraea242132019-02-25 12:50:09 +01001444 add_uaccess_safe(file);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001445
Peter Zijlstraff05ab22019-03-18 14:33:07 +01001446 ret = add_ignore_alternatives(file);
Josh Poimboeuf258c7602018-01-11 21:46:24 +00001447 if (ret)
1448 return ret;
1449
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001450 ret = add_jump_destinations(file);
1451 if (ret)
1452 return ret;
1453
Josh Poimboeufa845c7c2018-01-29 22:00:39 -06001454 ret = add_special_section_alts(file);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001455 if (ret)
1456 return ret;
1457
Josh Poimboeufa845c7c2018-01-29 22:00:39 -06001458 ret = add_call_destinations(file);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001459 if (ret)
1460 return ret;
1461
Josh Poimboeufe7c2bc32019-07-17 20:36:53 -05001462 ret = add_jump_table_alts(file);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001463 if (ret)
1464 return ret;
1465
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001466 ret = read_unwind_hints(file);
1467 if (ret)
1468 return ret;
1469
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01001470 ret = read_retpoline_hints(file);
1471 if (ret)
1472 return ret;
1473
Peter Zijlstrac4a33932020-03-10 18:57:41 +01001474 ret = read_instr_hints(file);
1475 if (ret)
1476 return ret;
1477
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001478 return 0;
1479}
1480
1481static bool is_fentry_call(struct instruction *insn)
1482{
1483 if (insn->type == INSN_CALL &&
1484 insn->call_dest->type == STT_NOTYPE &&
1485 !strcmp(insn->call_dest->name, "__fentry__"))
1486 return true;
1487
1488 return false;
1489}
1490
Peter Zijlstrae25eea82020-04-01 16:38:19 +02001491static bool has_modified_stack_frame(struct instruction *insn, struct insn_state *state)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001492{
Peter Zijlstrae25eea82020-04-01 16:38:19 +02001493 u8 ret_offset = insn->ret_offset;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001494 struct cfi_state *cfi = &state->cfi;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001495 int i;
1496
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001497 if (cfi->cfa.base != initial_func_cfi.cfa.base || cfi->drap)
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001498 return true;
1499
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001500 if (cfi->cfa.offset != initial_func_cfi.cfa.offset + ret_offset)
Peter Zijlstrae25eea82020-04-01 16:38:19 +02001501 return true;
1502
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001503 if (cfi->stack_size != initial_func_cfi.cfa.offset + ret_offset)
Peter Zijlstrae25eea82020-04-01 16:38:19 +02001504 return true;
1505
1506 for (i = 0; i < CFI_NUM_REGS; i++) {
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001507 if (cfi->regs[i].base != initial_func_cfi.regs[i].base ||
1508 cfi->regs[i].offset != initial_func_cfi.regs[i].offset)
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001509 return true;
Peter Zijlstrae25eea82020-04-01 16:38:19 +02001510 }
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001511
1512 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001513}
1514
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001515static bool has_valid_stack_frame(struct insn_state *state)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001516{
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001517 struct cfi_state *cfi = &state->cfi;
1518
1519 if (cfi->cfa.base == CFI_BP && cfi->regs[CFI_BP].base == CFI_CFA &&
1520 cfi->regs[CFI_BP].offset == -16)
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001521 return true;
1522
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001523 if (cfi->drap && cfi->regs[CFI_BP].base == CFI_BP)
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001524 return true;
1525
1526 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001527}
1528
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001529static int update_cfi_state_regs(struct instruction *insn,
1530 struct cfi_state *cfi,
Julien Thierry65ea47d2020-03-27 15:28:47 +00001531 struct stack_op *op)
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001532{
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001533 struct cfi_reg *cfa = &cfi->cfa;
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001534
1535 if (cfa->base != CFI_SP)
1536 return 0;
1537
1538 /* push */
Peter Zijlstraea242132019-02-25 12:50:09 +01001539 if (op->dest.type == OP_DEST_PUSH || op->dest.type == OP_DEST_PUSHF)
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001540 cfa->offset += 8;
1541
1542 /* pop */
Peter Zijlstraea242132019-02-25 12:50:09 +01001543 if (op->src.type == OP_SRC_POP || op->src.type == OP_SRC_POPF)
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001544 cfa->offset -= 8;
1545
1546 /* add immediate to sp */
1547 if (op->dest.type == OP_DEST_REG && op->src.type == OP_SRC_ADD &&
1548 op->dest.reg == CFI_SP && op->src.reg == CFI_SP)
1549 cfa->offset -= op->src.offset;
1550
1551 return 0;
1552}
1553
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001554static void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int offset)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001555{
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001556 if (arch_callee_saved_reg(reg) &&
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001557 cfi->regs[reg].base == CFI_UNDEFINED) {
1558 cfi->regs[reg].base = base;
1559 cfi->regs[reg].offset = offset;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001560 }
1561}
1562
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001563static void restore_reg(struct cfi_state *cfi, unsigned char reg)
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001564{
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001565 cfi->regs[reg].base = initial_func_cfi.regs[reg].base;
1566 cfi->regs[reg].offset = initial_func_cfi.regs[reg].offset;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001567}
1568
1569/*
1570 * A note about DRAP stack alignment:
1571 *
1572 * GCC has the concept of a DRAP register, which is used to help keep track of
1573 * the stack pointer when aligning the stack. r10 or r13 is used as the DRAP
1574 * register. The typical DRAP pattern is:
1575 *
1576 * 4c 8d 54 24 08 lea 0x8(%rsp),%r10
1577 * 48 83 e4 c0 and $0xffffffffffffffc0,%rsp
1578 * 41 ff 72 f8 pushq -0x8(%r10)
1579 * 55 push %rbp
1580 * 48 89 e5 mov %rsp,%rbp
1581 * (more pushes)
1582 * 41 52 push %r10
1583 * ...
1584 * 41 5a pop %r10
1585 * (more pops)
1586 * 5d pop %rbp
1587 * 49 8d 62 f8 lea -0x8(%r10),%rsp
1588 * c3 retq
1589 *
1590 * There are some variations in the epilogues, like:
1591 *
1592 * 5b pop %rbx
1593 * 41 5a pop %r10
1594 * 41 5c pop %r12
1595 * 41 5d pop %r13
1596 * 41 5e pop %r14
1597 * c9 leaveq
1598 * 49 8d 62 f8 lea -0x8(%r10),%rsp
1599 * c3 retq
1600 *
1601 * and:
1602 *
1603 * 4c 8b 55 e8 mov -0x18(%rbp),%r10
1604 * 48 8b 5d e0 mov -0x20(%rbp),%rbx
1605 * 4c 8b 65 f0 mov -0x10(%rbp),%r12
1606 * 4c 8b 6d f8 mov -0x8(%rbp),%r13
1607 * c9 leaveq
1608 * 49 8d 62 f8 lea -0x8(%r10),%rsp
1609 * c3 retq
1610 *
1611 * Sometimes r13 is used as the DRAP register, in which case it's saved and
1612 * restored beforehand:
1613 *
1614 * 41 55 push %r13
1615 * 4c 8d 6c 24 10 lea 0x10(%rsp),%r13
1616 * 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1617 * ...
1618 * 49 8d 65 f0 lea -0x10(%r13),%rsp
1619 * 41 5d pop %r13
1620 * c3 retq
1621 */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001622static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
Julien Thierry65ea47d2020-03-27 15:28:47 +00001623 struct stack_op *op)
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001624{
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001625 struct cfi_reg *cfa = &cfi->cfa;
1626 struct cfi_reg *regs = cfi->regs;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001627
1628 /* stack operations don't make sense with an undefined CFA */
1629 if (cfa->base == CFI_UNDEFINED) {
1630 if (insn->func) {
1631 WARN_FUNC("undefined stack state", insn->sec, insn->offset);
1632 return -1;
1633 }
1634 return 0;
1635 }
1636
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001637 if (cfi->type == ORC_TYPE_REGS || cfi->type == ORC_TYPE_REGS_IRET)
1638 return update_cfi_state_regs(insn, cfi, op);
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001639
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001640 switch (op->dest.type) {
1641
1642 case OP_DEST_REG:
1643 switch (op->src.type) {
1644
1645 case OP_SRC_REG:
Josh Poimboeuf0d0970e2017-09-20 16:24:32 -05001646 if (op->src.reg == CFI_SP && op->dest.reg == CFI_BP &&
1647 cfa->base == CFI_SP &&
1648 regs[CFI_BP].base == CFI_CFA &&
1649 regs[CFI_BP].offset == -cfa->offset) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001650
Josh Poimboeuf0d0970e2017-09-20 16:24:32 -05001651 /* mov %rsp, %rbp */
1652 cfa->base = op->dest.reg;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001653 cfi->bp_scratch = false;
Josh Poimboeuf0d0970e2017-09-20 16:24:32 -05001654 }
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001655
Josh Poimboeuf0d0970e2017-09-20 16:24:32 -05001656 else if (op->src.reg == CFI_SP &&
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001657 op->dest.reg == CFI_BP && cfi->drap) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001658
Josh Poimboeuf0d0970e2017-09-20 16:24:32 -05001659 /* drap: mov %rsp, %rbp */
1660 regs[CFI_BP].base = CFI_BP;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001661 regs[CFI_BP].offset = -cfi->stack_size;
1662 cfi->bp_scratch = false;
Josh Poimboeuf0d0970e2017-09-20 16:24:32 -05001663 }
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001664
Josh Poimboeuf0d0970e2017-09-20 16:24:32 -05001665 else if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
1666
1667 /*
1668 * mov %rsp, %reg
1669 *
1670 * This is needed for the rare case where GCC
1671 * does:
1672 *
1673 * mov %rsp, %rax
1674 * ...
1675 * mov %rax, %rsp
1676 */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001677 cfi->vals[op->dest.reg].base = CFI_CFA;
1678 cfi->vals[op->dest.reg].offset = -cfi->stack_size;
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001679 }
1680
Josh Poimboeuf3c1f0582018-03-22 13:00:37 -05001681 else if (op->src.reg == CFI_BP && op->dest.reg == CFI_SP &&
1682 cfa->base == CFI_BP) {
1683
1684 /*
1685 * mov %rbp, %rsp
1686 *
1687 * Restore the original stack pointer (Clang).
1688 */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001689 cfi->stack_size = -cfi->regs[CFI_BP].offset;
Josh Poimboeuf3c1f0582018-03-22 13:00:37 -05001690 }
1691
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001692 else if (op->dest.reg == cfa->base) {
1693
1694 /* mov %reg, %rsp */
1695 if (cfa->base == CFI_SP &&
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001696 cfi->vals[op->src.reg].base == CFI_CFA) {
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001697
1698 /*
1699 * This is needed for the rare case
1700 * where GCC does something dumb like:
1701 *
1702 * lea 0x8(%rsp), %rcx
1703 * ...
1704 * mov %rcx, %rsp
1705 */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001706 cfa->offset = -cfi->vals[op->src.reg].offset;
1707 cfi->stack_size = cfa->offset;
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001708
1709 } else {
1710 cfa->base = CFI_UNDEFINED;
1711 cfa->offset = 0;
1712 }
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001713 }
1714
1715 break;
1716
1717 case OP_SRC_ADD:
1718 if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) {
1719
1720 /* add imm, %rsp */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001721 cfi->stack_size -= op->src.offset;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001722 if (cfa->base == CFI_SP)
1723 cfa->offset -= op->src.offset;
1724 break;
1725 }
1726
1727 if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) {
1728
1729 /* lea disp(%rbp), %rsp */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001730 cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001731 break;
1732 }
1733
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001734 if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001735
1736 /* drap: lea disp(%rsp), %drap */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001737 cfi->drap_reg = op->dest.reg;
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001738
1739 /*
1740 * lea disp(%rsp), %reg
1741 *
1742 * This is needed for the rare case where GCC
1743 * does something dumb like:
1744 *
1745 * lea 0x8(%rsp), %rcx
1746 * ...
1747 * mov %rcx, %rsp
1748 */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001749 cfi->vals[op->dest.reg].base = CFI_CFA;
1750 cfi->vals[op->dest.reg].offset = \
1751 -cfi->stack_size + op->src.offset;
Josh Poimboeufdd88a0a2017-08-29 12:51:03 -05001752
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001753 break;
1754 }
1755
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001756 if (cfi->drap && op->dest.reg == CFI_SP &&
1757 op->src.reg == cfi->drap_reg) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001758
1759 /* drap: lea disp(%drap), %rsp */
1760 cfa->base = CFI_SP;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001761 cfa->offset = cfi->stack_size = -op->src.offset;
1762 cfi->drap_reg = CFI_UNDEFINED;
1763 cfi->drap = false;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001764 break;
1765 }
1766
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001767 if (op->dest.reg == cfi->cfa.base) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001768 WARN_FUNC("unsupported stack register modification",
1769 insn->sec, insn->offset);
1770 return -1;
1771 }
1772
1773 break;
1774
1775 case OP_SRC_AND:
1776 if (op->dest.reg != CFI_SP ||
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001777 (cfi->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
1778 (cfi->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001779 WARN_FUNC("unsupported stack pointer realignment",
1780 insn->sec, insn->offset);
1781 return -1;
1782 }
1783
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001784 if (cfi->drap_reg != CFI_UNDEFINED) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001785 /* drap: and imm, %rsp */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001786 cfa->base = cfi->drap_reg;
1787 cfa->offset = cfi->stack_size = 0;
1788 cfi->drap = true;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001789 }
1790
1791 /*
1792 * Older versions of GCC (4.8ish) realign the stack
1793 * without DRAP, with a frame pointer.
1794 */
1795
1796 break;
1797
1798 case OP_SRC_POP:
Peter Zijlstraea242132019-02-25 12:50:09 +01001799 case OP_SRC_POPF:
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001800 if (!cfi->drap && op->dest.reg == cfa->base) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001801
1802 /* pop %rbp */
1803 cfa->base = CFI_SP;
1804 }
1805
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001806 if (cfi->drap && cfa->base == CFI_BP_INDIRECT &&
1807 op->dest.reg == cfi->drap_reg &&
1808 cfi->drap_offset == -cfi->stack_size) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001809
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001810 /* drap: pop %drap */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001811 cfa->base = cfi->drap_reg;
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001812 cfa->offset = 0;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001813 cfi->drap_offset = -1;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001814
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001815 } else if (regs[op->dest.reg].offset == -cfi->stack_size) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001816
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001817 /* pop %reg */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001818 restore_reg(cfi, op->dest.reg);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001819 }
1820
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001821 cfi->stack_size -= 8;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001822 if (cfa->base == CFI_SP)
1823 cfa->offset -= 8;
1824
1825 break;
1826
1827 case OP_SRC_REG_INDIRECT:
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001828 if (cfi->drap && op->src.reg == CFI_BP &&
1829 op->src.offset == cfi->drap_offset) {
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001830
1831 /* drap: mov disp(%rbp), %drap */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001832 cfa->base = cfi->drap_reg;
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001833 cfa->offset = 0;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001834 cfi->drap_offset = -1;
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001835 }
1836
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001837 if (cfi->drap && op->src.reg == CFI_BP &&
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001838 op->src.offset == regs[op->dest.reg].offset) {
1839
1840 /* drap: mov disp(%rbp), %reg */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001841 restore_reg(cfi, op->dest.reg);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001842
1843 } else if (op->src.reg == cfa->base &&
1844 op->src.offset == regs[op->dest.reg].offset + cfa->offset) {
1845
1846 /* mov disp(%rbp), %reg */
1847 /* mov disp(%rsp), %reg */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001848 restore_reg(cfi, op->dest.reg);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001849 }
1850
1851 break;
1852
1853 default:
1854 WARN_FUNC("unknown stack-related instruction",
1855 insn->sec, insn->offset);
1856 return -1;
1857 }
1858
1859 break;
1860
1861 case OP_DEST_PUSH:
Peter Zijlstraea242132019-02-25 12:50:09 +01001862 case OP_DEST_PUSHF:
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001863 cfi->stack_size += 8;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001864 if (cfa->base == CFI_SP)
1865 cfa->offset += 8;
1866
1867 if (op->src.type != OP_SRC_REG)
1868 break;
1869
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001870 if (cfi->drap) {
1871 if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001872
1873 /* drap: push %drap */
1874 cfa->base = CFI_BP_INDIRECT;
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001875 cfa->offset = -cfi->stack_size;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001876
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001877 /* save drap so we know when to restore it */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001878 cfi->drap_offset = -cfi->stack_size;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001879
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001880 } else if (op->src.reg == CFI_BP && cfa->base == cfi->drap_reg) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001881
1882 /* drap: push %rbp */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001883 cfi->stack_size = 0;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001884
1885 } else if (regs[op->src.reg].base == CFI_UNDEFINED) {
1886
1887 /* drap: push %reg */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001888 save_reg(cfi, op->src.reg, CFI_BP, -cfi->stack_size);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001889 }
1890
1891 } else {
1892
1893 /* push %reg */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001894 save_reg(cfi, op->src.reg, CFI_CFA, -cfi->stack_size);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001895 }
1896
1897 /* detect when asm code uses rbp as a scratch register */
Josh Poimboeuf867ac9d2017-07-24 18:34:14 -05001898 if (!no_fp && insn->func && op->src.reg == CFI_BP &&
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001899 cfa->base != CFI_BP)
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001900 cfi->bp_scratch = true;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001901 break;
1902
1903 case OP_DEST_REG_INDIRECT:
1904
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001905 if (cfi->drap) {
1906 if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001907
1908 /* drap: mov %drap, disp(%rbp) */
1909 cfa->base = CFI_BP_INDIRECT;
1910 cfa->offset = op->dest.offset;
1911
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001912 /* save drap offset so we know when to restore it */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001913 cfi->drap_offset = op->dest.offset;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001914 }
1915
1916 else if (regs[op->src.reg].base == CFI_UNDEFINED) {
1917
1918 /* drap: mov reg, disp(%rbp) */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001919 save_reg(cfi, op->src.reg, CFI_BP, op->dest.offset);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001920 }
1921
1922 } else if (op->dest.reg == cfa->base) {
1923
1924 /* mov reg, disp(%rbp) */
1925 /* mov reg, disp(%rsp) */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001926 save_reg(cfi, op->src.reg, CFI_CFA,
1927 op->dest.offset - cfi->cfa.offset);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001928 }
1929
1930 break;
1931
1932 case OP_DEST_LEAVE:
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001933 if ((!cfi->drap && cfa->base != CFI_BP) ||
1934 (cfi->drap && cfa->base != cfi->drap_reg)) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001935 WARN_FUNC("leave instruction with modified stack frame",
1936 insn->sec, insn->offset);
1937 return -1;
1938 }
1939
1940 /* leave (mov %rbp, %rsp; pop %rbp) */
1941
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001942 cfi->stack_size = -cfi->regs[CFI_BP].offset - 8;
1943 restore_reg(cfi, CFI_BP);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001944
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001945 if (!cfi->drap) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001946 cfa->base = CFI_SP;
1947 cfa->offset -= 8;
1948 }
1949
1950 break;
1951
1952 case OP_DEST_MEM:
Peter Zijlstraea242132019-02-25 12:50:09 +01001953 if (op->src.type != OP_SRC_POP && op->src.type != OP_SRC_POPF) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001954 WARN_FUNC("unknown stack-related memory operation",
1955 insn->sec, insn->offset);
1956 return -1;
1957 }
1958
1959 /* pop mem */
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001960 cfi->stack_size -= 8;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001961 if (cfa->base == CFI_SP)
1962 cfa->offset -= 8;
1963
1964 break;
1965
1966 default:
1967 WARN_FUNC("unknown stack-related instruction",
1968 insn->sec, insn->offset);
1969 return -1;
1970 }
1971
1972 return 0;
1973}
1974
Julien Thierry65ea47d2020-03-27 15:28:47 +00001975static int handle_insn_ops(struct instruction *insn, struct insn_state *state)
1976{
1977 struct stack_op *op;
1978
1979 list_for_each_entry(op, &insn->stack_ops, list) {
1980 int res;
1981
Peter Zijlstrae7c02192020-03-25 14:04:45 +01001982 res = update_cfi_state(insn, &state->cfi, op);
Julien Thierry65ea47d2020-03-27 15:28:47 +00001983 if (res)
1984 return res;
1985
1986 if (op->dest.type == OP_DEST_PUSHF) {
1987 if (!state->uaccess_stack) {
1988 state->uaccess_stack = 1;
1989 } else if (state->uaccess_stack >> 31) {
1990 WARN_FUNC("PUSHF stack exhausted",
1991 insn->sec, insn->offset);
1992 return 1;
1993 }
1994 state->uaccess_stack <<= 1;
1995 state->uaccess_stack |= state->uaccess;
1996 }
1997
1998 if (op->src.type == OP_SRC_POPF) {
1999 if (state->uaccess_stack) {
2000 state->uaccess = state->uaccess_stack & 1;
2001 state->uaccess_stack >>= 1;
2002 if (state->uaccess_stack == 1)
2003 state->uaccess_stack = 0;
2004 }
2005 }
2006 }
2007
2008 return 0;
2009}
2010
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002011static bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2)
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002012{
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002013 struct cfi_state *cfi1 = &insn->cfi;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002014 int i;
2015
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002016 if (memcmp(&cfi1->cfa, &cfi2->cfa, sizeof(cfi1->cfa))) {
2017
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002018 WARN_FUNC("stack state mismatch: cfa1=%d%+d cfa2=%d%+d",
2019 insn->sec, insn->offset,
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002020 cfi1->cfa.base, cfi1->cfa.offset,
2021 cfi2->cfa.base, cfi2->cfa.offset);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002022
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002023 } else if (memcmp(&cfi1->regs, &cfi2->regs, sizeof(cfi1->regs))) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002024 for (i = 0; i < CFI_NUM_REGS; i++) {
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002025 if (!memcmp(&cfi1->regs[i], &cfi2->regs[i],
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002026 sizeof(struct cfi_reg)))
2027 continue;
2028
2029 WARN_FUNC("stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d",
2030 insn->sec, insn->offset,
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002031 i, cfi1->regs[i].base, cfi1->regs[i].offset,
2032 i, cfi2->regs[i].base, cfi2->regs[i].offset);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002033 break;
2034 }
2035
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002036 } else if (cfi1->type != cfi2->type) {
Josh Poimboeuf627fce12017-07-11 10:33:42 -05002037
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002038 WARN_FUNC("stack state mismatch: type1=%d type2=%d",
2039 insn->sec, insn->offset, cfi1->type, cfi2->type);
2040
2041 } else if (cfi1->drap != cfi2->drap ||
2042 (cfi1->drap && cfi1->drap_reg != cfi2->drap_reg) ||
2043 (cfi1->drap && cfi1->drap_offset != cfi2->drap_offset)) {
2044
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05002045 WARN_FUNC("stack state mismatch: drap1=%d(%d,%d) drap2=%d(%d,%d)",
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002046 insn->sec, insn->offset,
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002047 cfi1->drap, cfi1->drap_reg, cfi1->drap_offset,
2048 cfi2->drap, cfi2->drap_reg, cfi2->drap_offset);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002049
2050 } else
2051 return true;
2052
2053 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002054}
2055
Peter Zijlstraea242132019-02-25 12:50:09 +01002056static inline bool func_uaccess_safe(struct symbol *func)
2057{
2058 if (func)
Josh Poimboeufe10cd8f2019-07-17 20:36:48 -05002059 return func->uaccess_safe;
Peter Zijlstraea242132019-02-25 12:50:09 +01002060
2061 return false;
2062}
2063
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -05002064static inline const char *call_dest_name(struct instruction *insn)
Peter Zijlstraea242132019-02-25 12:50:09 +01002065{
2066 if (insn->call_dest)
2067 return insn->call_dest->name;
2068
2069 return "{dynamic}";
2070}
2071
2072static int validate_call(struct instruction *insn, struct insn_state *state)
2073{
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002074 if (state->noinstr && state->instr <= 0 &&
Thomas Gleixner0cc9ac8d2020-03-25 17:18:17 +01002075 (!insn->call_dest || !insn->call_dest->sec->noinstr)) {
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002076 WARN_FUNC("call to %s() leaves .noinstr.text section",
2077 insn->sec, insn->offset, call_dest_name(insn));
2078 return 1;
2079 }
2080
Peter Zijlstraea242132019-02-25 12:50:09 +01002081 if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
2082 WARN_FUNC("call to %s() with UACCESS enabled",
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -05002083 insn->sec, insn->offset, call_dest_name(insn));
Peter Zijlstraea242132019-02-25 12:50:09 +01002084 return 1;
2085 }
2086
Peter Zijlstra2f0f9e92019-02-25 11:10:55 +01002087 if (state->df) {
2088 WARN_FUNC("call to %s() with DF set",
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -05002089 insn->sec, insn->offset, call_dest_name(insn));
Peter Zijlstra2f0f9e92019-02-25 11:10:55 +01002090 return 1;
2091 }
2092
Peter Zijlstraea242132019-02-25 12:50:09 +01002093 return 0;
2094}
2095
Peter Zijlstra54262aa2019-03-06 12:58:15 +01002096static int validate_sibling_call(struct instruction *insn, struct insn_state *state)
2097{
Peter Zijlstrae25eea82020-04-01 16:38:19 +02002098 if (has_modified_stack_frame(insn, state)) {
Peter Zijlstra54262aa2019-03-06 12:58:15 +01002099 WARN_FUNC("sibling call from callable instruction with modified stack frame",
2100 insn->sec, insn->offset);
2101 return 1;
2102 }
2103
Peter Zijlstraea242132019-02-25 12:50:09 +01002104 return validate_call(insn, state);
Peter Zijlstra54262aa2019-03-06 12:58:15 +01002105}
2106
Peter Zijlstraa92e92d2020-03-10 18:07:44 +01002107static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
2108{
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002109 if (state->noinstr && state->instr > 0) {
2110 WARN_FUNC("return with instrumentation enabled",
2111 insn->sec, insn->offset);
2112 return 1;
2113 }
2114
Peter Zijlstraa92e92d2020-03-10 18:07:44 +01002115 if (state->uaccess && !func_uaccess_safe(func)) {
2116 WARN_FUNC("return with UACCESS enabled",
2117 insn->sec, insn->offset);
2118 return 1;
2119 }
2120
2121 if (!state->uaccess && func_uaccess_safe(func)) {
2122 WARN_FUNC("return with UACCESS disabled from a UACCESS-safe function",
2123 insn->sec, insn->offset);
2124 return 1;
2125 }
2126
2127 if (state->df) {
2128 WARN_FUNC("return with DF set",
2129 insn->sec, insn->offset);
2130 return 1;
2131 }
2132
Peter Zijlstrae25eea82020-04-01 16:38:19 +02002133 if (func && has_modified_stack_frame(insn, state)) {
Peter Zijlstraa92e92d2020-03-10 18:07:44 +01002134 WARN_FUNC("return with modified stack frame",
2135 insn->sec, insn->offset);
2136 return 1;
2137 }
2138
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002139 if (state->cfi.bp_scratch) {
Josh Poimboeufb2966952020-04-01 13:23:29 -05002140 WARN_FUNC("BP used as a scratch register",
2141 insn->sec, insn->offset);
Peter Zijlstraa92e92d2020-03-10 18:07:44 +01002142 return 1;
2143 }
2144
2145 return 0;
2146}
2147
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002148/*
2149 * Follow the branch starting at the given instruction, and recursively follow
2150 * any other branches (jumps). Meanwhile, track the frame pointer state at
2151 * each instruction and validate all the rules described in
2152 * tools/objtool/Documentation/stack-validation.txt.
2153 */
Josh Poimboeufc705cec2019-07-17 20:36:47 -05002154static int validate_branch(struct objtool_file *file, struct symbol *func,
Peter Zijlstrab7460462020-04-02 10:15:51 +02002155 struct instruction *insn, struct insn_state state)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002156{
2157 struct alternative *alt;
Peter Zijlstrab7460462020-04-02 10:15:51 +02002158 struct instruction *next_insn;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002159 struct section *sec;
Peter Zijlstra882a0db2019-07-24 17:47:26 -05002160 u8 visited;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002161 int ret;
2162
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002163 sec = insn->sec;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002164
2165 if (insn->alt_group && list_empty(&insn->alts)) {
2166 WARN_FUNC("don't know how to handle branch to middle of alternative instruction group",
2167 sec, insn->offset);
Josh Poimboeuf12b25722017-08-10 16:37:25 -05002168 return 1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002169 }
2170
2171 while (1) {
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002172 next_insn = next_insn_same_sec(file, insn);
2173
Josh Poimboeuf13810432018-05-09 22:39:15 -05002174 if (file->c_file && func && insn->func && func != insn->func->pfunc) {
Josh Poimboeufee976382017-08-11 12:24:15 -05002175 WARN("%s() falls through to next function %s()",
2176 func->name, insn->func->name);
2177 return 1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002178 }
2179
Josh Poimboeuf48550222017-07-07 09:19:42 -05002180 if (func && insn->ignore) {
2181 WARN_FUNC("BUG: why am I validating an ignored function?",
2182 sec, insn->offset);
Josh Poimboeuf12b25722017-08-10 16:37:25 -05002183 return 1;
Josh Poimboeuf48550222017-07-07 09:19:42 -05002184 }
2185
Peter Zijlstra882a0db2019-07-24 17:47:26 -05002186 visited = 1 << state.uaccess;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002187 if (insn->visited) {
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002188 if (!insn->hint && !insn_cfi_match(insn, &state.cfi))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002189 return 1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002190
Peter Zijlstra882a0db2019-07-24 17:47:26 -05002191 if (insn->visited & visited)
Peter Zijlstraea242132019-02-25 12:50:09 +01002192 return 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002193 }
2194
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002195 if (state.noinstr)
2196 state.instr += insn->instr;
2197
Peter Zijlstrac536ed22020-04-01 16:54:26 +02002198 if (insn->hint)
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002199 state.cfi = insn->cfi;
Peter Zijlstrac536ed22020-04-01 16:54:26 +02002200 else
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002201 insn->cfi = state.cfi;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002202
Peter Zijlstra882a0db2019-07-24 17:47:26 -05002203 insn->visited |= visited;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002204
Josh Poimboeufa845c7c2018-01-29 22:00:39 -06002205 if (!insn->ignore_alts) {
Peter Zijlstra764eef42019-03-01 11:19:03 +01002206 bool skip_orig = false;
2207
Josh Poimboeufa845c7c2018-01-29 22:00:39 -06002208 list_for_each_entry(alt, &insn->alts, list) {
Peter Zijlstra764eef42019-03-01 11:19:03 +01002209 if (alt->skip_orig)
2210 skip_orig = true;
2211
Josh Poimboeufc705cec2019-07-17 20:36:47 -05002212 ret = validate_branch(file, func, alt->insn, state);
Peter Zijlstra7697eee2019-03-01 11:15:49 +01002213 if (ret) {
2214 if (backtrace)
2215 BT_FUNC("(alt)", insn);
2216 return ret;
2217 }
Josh Poimboeufa845c7c2018-01-29 22:00:39 -06002218 }
Peter Zijlstra764eef42019-03-01 11:19:03 +01002219
2220 if (skip_orig)
2221 return 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002222 }
2223
2224 switch (insn->type) {
2225
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002226 case INSN_RETURN:
Peter Zijlstraa92e92d2020-03-10 18:07:44 +01002227 return validate_return(func, insn, &state);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002228
2229 case INSN_CALL:
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002230 case INSN_CALL_DYNAMIC:
Peter Zijlstraea242132019-02-25 12:50:09 +01002231 ret = validate_call(insn, &state);
2232 if (ret)
2233 return ret;
2234
Josh Poimboeufc9bab222019-07-17 20:36:51 -05002235 if (!no_fp && func && !is_fentry_call(insn) &&
2236 !has_valid_stack_frame(&state)) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002237 WARN_FUNC("call without frame pointer save/setup",
2238 sec, insn->offset);
2239 return 1;
2240 }
Josh Poimboeufc9bab222019-07-17 20:36:51 -05002241
2242 if (dead_end_function(file, insn->call_dest))
2243 return 0;
2244
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002245 break;
2246
2247 case INSN_JUMP_CONDITIONAL:
2248 case INSN_JUMP_UNCONDITIONAL:
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -05002249 if (func && is_sibling_call(insn)) {
Peter Zijlstra54262aa2019-03-06 12:58:15 +01002250 ret = validate_sibling_call(insn, &state);
2251 if (ret)
2252 return ret;
2253
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -05002254 } else if (insn->jump_dest) {
Josh Poimboeufc705cec2019-07-17 20:36:47 -05002255 ret = validate_branch(file, func,
2256 insn->jump_dest, state);
Peter Zijlstra7697eee2019-03-01 11:15:49 +01002257 if (ret) {
2258 if (backtrace)
2259 BT_FUNC("(branch)", insn);
2260 return ret;
2261 }
Josh Poimboeuf48550222017-07-07 09:19:42 -05002262 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002263
2264 if (insn->type == INSN_JUMP_UNCONDITIONAL)
2265 return 0;
2266
2267 break;
2268
2269 case INSN_JUMP_DYNAMIC:
Josh Poimboeufb68b9902019-07-17 20:36:57 -05002270 case INSN_JUMP_DYNAMIC_CONDITIONAL:
Josh Poimboeuf0c1ddd32019-07-17 20:36:52 -05002271 if (func && is_sibling_call(insn)) {
Peter Zijlstra54262aa2019-03-06 12:58:15 +01002272 ret = validate_sibling_call(insn, &state);
2273 if (ret)
2274 return ret;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002275 }
2276
Josh Poimboeufb68b9902019-07-17 20:36:57 -05002277 if (insn->type == INSN_JUMP_DYNAMIC)
2278 return 0;
2279
2280 break;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002281
Peter Zijlstrab7460462020-04-02 10:15:51 +02002282 case INSN_EXCEPTION_RETURN:
2283 if (handle_insn_ops(insn, &state))
2284 return 1;
2285
2286 /*
2287 * This handles x86's sync_core() case, where we use an
2288 * IRET to self. All 'normal' IRET instructions are in
2289 * STT_NOTYPE entry symbols.
2290 */
2291 if (func)
2292 break;
2293
2294 return 0;
2295
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002296 case INSN_CONTEXT_SWITCH:
2297 if (func && (!next_insn || !next_insn->hint)) {
2298 WARN_FUNC("unsupported instruction in callable function",
2299 sec, insn->offset);
2300 return 1;
2301 }
2302 return 0;
2303
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002304 case INSN_STACK:
Julien Thierry65ea47d2020-03-27 15:28:47 +00002305 if (handle_insn_ops(insn, &state))
Josh Poimboeuf12b25722017-08-10 16:37:25 -05002306 return 1;
Peter Zijlstraea242132019-02-25 12:50:09 +01002307 break;
2308
2309 case INSN_STAC:
2310 if (state.uaccess) {
2311 WARN_FUNC("recursive UACCESS enable", sec, insn->offset);
2312 return 1;
2313 }
2314
2315 state.uaccess = true;
2316 break;
2317
2318 case INSN_CLAC:
Josh Poimboeufc705cec2019-07-17 20:36:47 -05002319 if (!state.uaccess && func) {
Peter Zijlstraea242132019-02-25 12:50:09 +01002320 WARN_FUNC("redundant UACCESS disable", sec, insn->offset);
2321 return 1;
2322 }
2323
2324 if (func_uaccess_safe(func) && !state.uaccess_stack) {
2325 WARN_FUNC("UACCESS-safe disables UACCESS", sec, insn->offset);
2326 return 1;
2327 }
2328
2329 state.uaccess = false;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002330 break;
2331
Peter Zijlstra2f0f9e92019-02-25 11:10:55 +01002332 case INSN_STD:
2333 if (state.df)
2334 WARN_FUNC("recursive STD", sec, insn->offset);
2335
2336 state.df = true;
2337 break;
2338
2339 case INSN_CLD:
Josh Poimboeufc705cec2019-07-17 20:36:47 -05002340 if (!state.df && func)
Peter Zijlstra2f0f9e92019-02-25 11:10:55 +01002341 WARN_FUNC("redundant CLD", sec, insn->offset);
2342
2343 state.df = false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002344 break;
2345
2346 default:
2347 break;
2348 }
2349
2350 if (insn->dead_end)
2351 return 0;
2352
Josh Poimboeuf00d96182017-09-18 21:43:30 -05002353 if (!next_insn) {
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002354 if (state.cfi.cfa.base == CFI_UNDEFINED)
Josh Poimboeuf00d96182017-09-18 21:43:30 -05002355 return 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002356 WARN("%s: unexpected end of section", sec->name);
2357 return 1;
2358 }
Josh Poimboeuf00d96182017-09-18 21:43:30 -05002359
2360 insn = next_insn;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002361 }
2362
2363 return 0;
2364}
2365
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002366static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002367{
2368 struct instruction *insn;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002369 struct insn_state state;
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002370 int ret, warnings = 0;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002371
2372 if (!file->hints)
2373 return 0;
2374
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002375 init_insn_state(&state, sec);
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002376
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002377 if (sec) {
2378 insn = find_insn(file, sec, 0);
2379 if (!insn)
2380 return 0;
2381 } else {
2382 insn = list_first_entry(&file->insn_list, typeof(*insn), list);
2383 }
2384
2385 while (&insn->list != &file->insn_list && (!sec || insn->sec == sec)) {
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002386 if (insn->hint && !insn->visited) {
Josh Poimboeufc705cec2019-07-17 20:36:47 -05002387 ret = validate_branch(file, insn->func, insn, state);
Peter Zijlstra7697eee2019-03-01 11:15:49 +01002388 if (ret && backtrace)
2389 BT_FUNC("<=== (hint)", insn);
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002390 warnings += ret;
2391 }
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002392
2393 insn = list_next_entry(insn, list);
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002394 }
2395
2396 return warnings;
2397}
2398
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01002399static int validate_retpoline(struct objtool_file *file)
2400{
2401 struct instruction *insn;
2402 int warnings = 0;
2403
2404 for_each_insn(file, insn) {
2405 if (insn->type != INSN_JUMP_DYNAMIC &&
2406 insn->type != INSN_CALL_DYNAMIC)
2407 continue;
2408
2409 if (insn->retpoline_safe)
2410 continue;
2411
Peter Zijlstraca41b972018-01-31 10:18:28 +01002412 /*
2413 * .init.text code is ran before userspace and thus doesn't
2414 * strictly need retpolines, except for modules which are
2415 * loaded late, they very much do need retpoline in their
2416 * .init.text
2417 */
2418 if (!strcmp(insn->sec->name, ".init.text") && !module)
2419 continue;
2420
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01002421 WARN_FUNC("indirect %s found in RETPOLINE build",
2422 insn->sec, insn->offset,
2423 insn->type == INSN_JUMP_DYNAMIC ? "jump" : "call");
2424
2425 warnings++;
2426 }
2427
2428 return warnings;
2429}
2430
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002431static bool is_kasan_insn(struct instruction *insn)
2432{
2433 return (insn->type == INSN_CALL &&
2434 !strcmp(insn->call_dest->name, "__asan_handle_no_return"));
2435}
2436
2437static bool is_ubsan_insn(struct instruction *insn)
2438{
2439 return (insn->type == INSN_CALL &&
2440 !strcmp(insn->call_dest->name,
2441 "__ubsan_handle_builtin_unreachable"));
2442}
2443
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002444static bool ignore_unreachable_insn(struct instruction *insn)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002445{
2446 int i;
2447
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002448 if (insn->ignore || insn->type == INSN_NOP)
2449 return true;
2450
2451 /*
2452 * Ignore any unused exceptions. This can happen when a whitelisted
2453 * function has an exception table entry.
Josh Poimboeuf0e2bb2b2017-07-27 15:56:54 -05002454 *
2455 * Also ignore alternative replacement instructions. This can happen
2456 * when a whitelisted function uses one of the ALTERNATIVE macros.
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002457 */
Josh Poimboeuf0e2bb2b2017-07-27 15:56:54 -05002458 if (!strcmp(insn->sec->name, ".fixup") ||
2459 !strcmp(insn->sec->name, ".altinstr_replacement") ||
2460 !strcmp(insn->sec->name, ".altinstr_aux"))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002461 return true;
2462
Josh Poimboeufbd841d62020-04-01 13:23:25 -05002463 if (!insn->func)
2464 return false;
2465
2466 /*
2467 * CONFIG_UBSAN_TRAP inserts a UD2 when it sees
2468 * __builtin_unreachable(). The BUG() macro has an unreachable() after
2469 * the UD2, which causes GCC's undefined trap logic to emit another UD2
2470 * (or occasionally a JMP to UD2).
2471 */
2472 if (list_prev_entry(insn, list)->dead_end &&
2473 (insn->type == INSN_BUG ||
2474 (insn->type == INSN_JUMP_UNCONDITIONAL &&
2475 insn->jump_dest && insn->jump_dest->type == INSN_BUG)))
2476 return true;
2477
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002478 /*
2479 * Check if this (or a subsequent) instruction is related to
2480 * CONFIG_UBSAN or CONFIG_KASAN.
2481 *
2482 * End the search at 5 instructions to avoid going into the weeds.
2483 */
2484 for (i = 0; i < 5; i++) {
2485
2486 if (is_kasan_insn(insn) || is_ubsan_insn(insn))
2487 return true;
2488
Josh Poimboeuffe24e272018-02-08 17:09:25 -06002489 if (insn->type == INSN_JUMP_UNCONDITIONAL) {
2490 if (insn->jump_dest &&
2491 insn->jump_dest->func == insn->func) {
2492 insn = insn->jump_dest;
2493 continue;
2494 }
2495
2496 break;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002497 }
2498
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002499 if (insn->offset + insn->len >= insn->func->offset + insn->func->len)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002500 break;
Josh Poimboeuffe24e272018-02-08 17:09:25 -06002501
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002502 insn = list_next_entry(insn, list);
2503 }
2504
2505 return false;
2506}
2507
Peter Zijlstra4b5e2e72020-03-23 21:17:50 +01002508static int validate_symbol(struct objtool_file *file, struct section *sec,
2509 struct symbol *sym, struct insn_state *state)
2510{
2511 struct instruction *insn;
2512 int ret;
2513
2514 if (!sym->len) {
2515 WARN("%s() is missing an ELF size annotation", sym->name);
2516 return 1;
2517 }
2518
2519 if (sym->pfunc != sym || sym->alias != sym)
2520 return 0;
2521
2522 insn = find_insn(file, sec, sym->offset);
2523 if (!insn || insn->ignore || insn->visited)
2524 return 0;
2525
2526 state->uaccess = sym->uaccess_safe;
2527
2528 ret = validate_branch(file, insn->func, insn, *state);
2529 if (ret && backtrace)
2530 BT_FUNC("<=== (sym)", insn);
2531 return ret;
2532}
2533
Peter Zijlstra350994b2020-03-23 20:57:13 +01002534static int validate_section(struct objtool_file *file, struct section *sec)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002535{
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002536 struct insn_state state;
Peter Zijlstra4b5e2e72020-03-23 21:17:50 +01002537 struct symbol *func;
2538 int warnings = 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002539
Peter Zijlstra350994b2020-03-23 20:57:13 +01002540 list_for_each_entry(func, &sec->symbol_list, list) {
2541 if (func->type != STT_FUNC)
2542 continue;
Josh Poimboeufe10cd8f2019-07-17 20:36:48 -05002543
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002544 init_insn_state(&state, sec);
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002545 state.cfi.cfa = initial_func_cfi.cfa;
2546 memcpy(&state.cfi.regs, &initial_func_cfi.regs,
Julien Thierry0699e552020-03-27 15:28:40 +00002547 CFI_NUM_REGS * sizeof(struct cfi_reg));
Peter Zijlstrae7c02192020-03-25 14:04:45 +01002548 state.cfi.stack_size = initial_func_cfi.cfa.offset;
Julien Thierry0699e552020-03-27 15:28:40 +00002549
Peter Zijlstra4b5e2e72020-03-23 21:17:50 +01002550 warnings += validate_symbol(file, sec, func, &state);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002551 }
2552
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002553 return warnings;
2554}
2555
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002556static int validate_vmlinux_functions(struct objtool_file *file)
2557{
2558 struct section *sec;
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002559 int warnings = 0;
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002560
2561 sec = find_section_by_name(file->elf, ".noinstr.text");
Thomas Gleixner0cc9ac8d2020-03-25 17:18:17 +01002562 if (sec) {
2563 warnings += validate_section(file, sec);
2564 warnings += validate_unwind_hints(file, sec);
2565 }
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002566
Thomas Gleixner0cc9ac8d2020-03-25 17:18:17 +01002567 sec = find_section_by_name(file->elf, ".entry.text");
2568 if (sec) {
2569 warnings += validate_section(file, sec);
2570 warnings += validate_unwind_hints(file, sec);
2571 }
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002572
2573 return warnings;
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002574}
2575
Peter Zijlstra350994b2020-03-23 20:57:13 +01002576static int validate_functions(struct objtool_file *file)
2577{
2578 struct section *sec;
2579 int warnings = 0;
2580
Peter Zijlstrada837bd2020-03-23 21:11:14 +01002581 for_each_sec(file, sec) {
2582 if (!(sec->sh.sh_flags & SHF_EXECINSTR))
2583 continue;
2584
Peter Zijlstra350994b2020-03-23 20:57:13 +01002585 warnings += validate_section(file, sec);
Peter Zijlstrada837bd2020-03-23 21:11:14 +01002586 }
Peter Zijlstra350994b2020-03-23 20:57:13 +01002587
2588 return warnings;
2589}
2590
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002591static int validate_reachable_instructions(struct objtool_file *file)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002592{
2593 struct instruction *insn;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002594
2595 if (file->ignore_unreachables)
2596 return 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002597
2598 for_each_insn(file, insn) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002599 if (insn->visited || ignore_unreachable_insn(insn))
2600 continue;
2601
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002602 WARN_FUNC("unreachable instruction", insn->sec, insn->offset);
2603 return 1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002604 }
2605
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002606 return 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002607}
2608
Josh Poimboeuf0c671812019-03-18 19:09:38 -05002609static struct objtool_file file;
2610
Peter Zijlstra43a45252018-01-16 17:16:32 +01002611int check(const char *_objname, bool orc)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002612{
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002613 int ret, warnings = 0;
2614
2615 objname = _objname;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002616
Michael Forney8e144792019-07-10 16:20:11 -05002617 file.elf = elf_read(objname, orc ? O_RDWR : O_RDONLY);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002618 if (!file.elf)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002619 return 1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002620
2621 INIT_LIST_HEAD(&file.insn_list);
2622 hash_init(file.insn_hash);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002623 file.c_file = find_section_by_name(file.elf, ".comment");
Josh Poimboeuf867ac9d2017-07-24 18:34:14 -05002624 file.ignore_unreachables = no_unreachable;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002625 file.hints = false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002626
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002627 arch_initial_func_cfi_state(&initial_func_cfi);
2628
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002629 ret = decode_sections(&file);
2630 if (ret < 0)
2631 goto out;
2632 warnings += ret;
2633
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002634 if (list_empty(&file.insn_list))
2635 goto out;
2636
Peter Zijlstrac4a33932020-03-10 18:57:41 +01002637 if (vmlinux && !validate_dup) {
2638 ret = validate_vmlinux_functions(&file);
2639 if (ret < 0)
2640 goto out;
2641
2642 warnings += ret;
2643 goto out;
2644 }
2645
Peter Zijlstrab5bc2232018-01-16 10:24:06 +01002646 if (retpoline) {
2647 ret = validate_retpoline(&file);
2648 if (ret < 0)
2649 return ret;
2650 warnings += ret;
2651 }
2652
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002653 ret = validate_functions(&file);
2654 if (ret < 0)
2655 goto out;
2656 warnings += ret;
2657
Peter Zijlstra932f8e92020-03-23 18:26:03 +01002658 ret = validate_unwind_hints(&file, NULL);
Josh Poimboeuf39358a02017-07-11 10:33:43 -05002659 if (ret < 0)
2660 goto out;
2661 warnings += ret;
2662
Josh Poimboeufbaa41462017-06-28 10:11:07 -05002663 if (!warnings) {
2664 ret = validate_reachable_instructions(&file);
2665 if (ret < 0)
2666 goto out;
2667 warnings += ret;
2668 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002669
Josh Poimboeuf627fce12017-07-11 10:33:42 -05002670 if (orc) {
2671 ret = create_orc(&file);
2672 if (ret < 0)
2673 goto out;
2674
2675 ret = create_orc_sections(&file);
2676 if (ret < 0)
2677 goto out;
2678
2679 ret = elf_write(file.elf);
2680 if (ret < 0)
2681 goto out;
2682 }
2683
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002684out:
Josh Poimboeuf644592d2020-02-10 12:32:38 -06002685 if (ret < 0) {
2686 /*
2687 * Fatal error. The binary is corrupt or otherwise broken in
2688 * some way, or objtool itself is broken. Fail the kernel
2689 * build.
2690 */
2691 return ret;
2692 }
2693
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05002694 return 0;
2695}