blob: 3dffeb9445237429a004bb9657eb1a2ade2d3659 [file] [log] [blame]
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001/*
2 * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <string.h>
19#include <stdlib.h>
20
21#include "check.h"
22#include "elf.h"
23#include "special.h"
24#include "arch.h"
25#include "warn.h"
26
27#include <linux/hashtable.h>
28#include <linux/kernel.h>
29
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050030struct alternative {
31 struct list_head list;
32 struct instruction *insn;
33};
34
35const char *objname;
Josh Poimboeuf867ac9d2017-07-24 18:34:14 -050036static bool no_fp;
Josh Poimboeufbaa41462017-06-28 10:11:07 -050037struct cfi_state initial_func_cfi;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050038
Josh Poimboeuf627fce12017-07-11 10:33:42 -050039struct instruction *find_insn(struct objtool_file *file,
40 struct section *sec, unsigned long offset)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050041{
42 struct instruction *insn;
43
44 hash_for_each_possible(file->insn_hash, insn, hash, offset)
45 if (insn->sec == sec && insn->offset == offset)
46 return insn;
47
48 return NULL;
49}
50
51static struct instruction *next_insn_same_sec(struct objtool_file *file,
52 struct instruction *insn)
53{
54 struct instruction *next = list_next_entry(insn, list);
55
Josh Poimboeufbaa41462017-06-28 10:11:07 -050056 if (!next || &next->list == &file->insn_list || next->sec != insn->sec)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050057 return NULL;
58
59 return next;
60}
61
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050062#define func_for_each_insn(file, func, insn) \
63 for (insn = find_insn(file, func->sec, func->offset); \
64 insn && &insn->list != &file->insn_list && \
65 insn->sec == func->sec && \
66 insn->offset < func->offset + func->len; \
67 insn = list_next_entry(insn, list))
68
69#define func_for_each_insn_continue_reverse(file, func, insn) \
70 for (insn = list_prev_entry(insn, list); \
71 &insn->list != &file->insn_list && \
72 insn->sec == func->sec && insn->offset >= func->offset; \
73 insn = list_prev_entry(insn, list))
74
75#define sec_for_each_insn_from(file, insn) \
76 for (; insn; insn = next_insn_same_sec(file, insn))
77
Josh Poimboeufbaa41462017-06-28 10:11:07 -050078#define sec_for_each_insn_continue(file, insn) \
79 for (insn = next_insn_same_sec(file, insn); insn; \
80 insn = next_insn_same_sec(file, insn))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050081
82/*
83 * Check if the function has been manually whitelisted with the
84 * STACK_FRAME_NON_STANDARD macro, or if it should be automatically whitelisted
85 * due to its use of a context switching instruction.
86 */
87static bool ignore_func(struct objtool_file *file, struct symbol *func)
88{
89 struct rela *rela;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -050090
91 /* check for STACK_FRAME_NON_STANDARD */
92 if (file->whitelist && file->whitelist->rela)
93 list_for_each_entry(rela, &file->whitelist->rela->rela_list, list) {
94 if (rela->sym->type == STT_SECTION &&
95 rela->sym->sec == func->sec &&
96 rela->addend == func->offset)
97 return true;
98 if (rela->sym->type == STT_FUNC && rela->sym == func)
99 return true;
100 }
101
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500102 return false;
103}
104
105/*
106 * This checks to see if the given function is a "noreturn" function.
107 *
108 * For global functions which are outside the scope of this object file, we
109 * have to keep a manual list of them.
110 *
111 * For local functions, we have to detect them manually by simply looking for
112 * the lack of a return instruction.
113 *
114 * Returns:
115 * -1: error
116 * 0: no dead end
117 * 1: dead end
118 */
119static int __dead_end_function(struct objtool_file *file, struct symbol *func,
120 int recursion)
121{
122 int i;
123 struct instruction *insn;
124 bool empty = true;
125
126 /*
127 * Unfortunately these have to be hard coded because the noreturn
128 * attribute isn't provided in ELF data.
129 */
130 static const char * const global_noreturns[] = {
131 "__stack_chk_fail",
132 "panic",
133 "do_exit",
134 "do_task_dead",
135 "__module_put_and_exit",
136 "complete_and_exit",
137 "kvm_spurious_fault",
138 "__reiserfs_panic",
139 "lbug_with_loc",
140 "fortify_panic",
141 };
142
143 if (func->bind == STB_WEAK)
144 return 0;
145
146 if (func->bind == STB_GLOBAL)
147 for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
148 if (!strcmp(func->name, global_noreturns[i]))
149 return 1;
150
151 if (!func->sec)
152 return 0;
153
154 func_for_each_insn(file, func, insn) {
155 empty = false;
156
157 if (insn->type == INSN_RETURN)
158 return 0;
159 }
160
161 if (empty)
162 return 0;
163
164 /*
165 * A function can have a sibling call instead of a return. In that
166 * case, the function's dead-end status depends on whether the target
167 * of the sibling call returns.
168 */
169 func_for_each_insn(file, func, insn) {
170 if (insn->sec != func->sec ||
171 insn->offset >= func->offset + func->len)
172 break;
173
174 if (insn->type == INSN_JUMP_UNCONDITIONAL) {
175 struct instruction *dest = insn->jump_dest;
176 struct symbol *dest_func;
177
178 if (!dest)
179 /* sibling call to another file */
180 return 0;
181
182 if (dest->sec != func->sec ||
183 dest->offset < func->offset ||
184 dest->offset >= func->offset + func->len) {
185 /* local sibling call */
186 dest_func = find_symbol_by_offset(dest->sec,
187 dest->offset);
188 if (!dest_func)
189 continue;
190
191 if (recursion == 5) {
192 WARN_FUNC("infinite recursion (objtool bug!)",
193 dest->sec, dest->offset);
194 return -1;
195 }
196
197 return __dead_end_function(file, dest_func,
198 recursion + 1);
199 }
200 }
201
202 if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts))
203 /* sibling call */
204 return 0;
205 }
206
207 return 1;
208}
209
210static int dead_end_function(struct objtool_file *file, struct symbol *func)
211{
212 return __dead_end_function(file, func, 0);
213}
214
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500215static void clear_insn_state(struct insn_state *state)
216{
217 int i;
218
219 memset(state, 0, sizeof(*state));
220 state->cfa.base = CFI_UNDEFINED;
221 for (i = 0; i < CFI_NUM_REGS; i++)
222 state->regs[i].base = CFI_UNDEFINED;
223 state->drap_reg = CFI_UNDEFINED;
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -0500224 state->drap_offset = -1;
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500225}
226
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500227/*
228 * Call the arch-specific instruction decoder for all the instructions and add
229 * them to the global instruction list.
230 */
231static int decode_instructions(struct objtool_file *file)
232{
233 struct section *sec;
234 struct symbol *func;
235 unsigned long offset;
236 struct instruction *insn;
237 int ret;
238
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500239 for_each_sec(file, sec) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500240
241 if (!(sec->sh.sh_flags & SHF_EXECINSTR))
242 continue;
243
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500244 if (strcmp(sec->name, ".altinstr_replacement") &&
245 strcmp(sec->name, ".altinstr_aux") &&
246 strncmp(sec->name, ".discard.", 9))
247 sec->text = true;
248
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500249 for (offset = 0; offset < sec->len; offset += insn->len) {
250 insn = malloc(sizeof(*insn));
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500251 if (!insn) {
252 WARN("malloc failed");
253 return -1;
254 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500255 memset(insn, 0, sizeof(*insn));
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500256 INIT_LIST_HEAD(&insn->alts);
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500257 clear_insn_state(&insn->state);
258
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500259 insn->sec = sec;
260 insn->offset = offset;
261
262 ret = arch_decode_instruction(file->elf, sec, offset,
263 sec->len - offset,
264 &insn->len, &insn->type,
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500265 &insn->immediate,
266 &insn->stack_op);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500267 if (ret)
268 return ret;
269
270 if (!insn->type || insn->type > INSN_LAST) {
271 WARN_FUNC("invalid instruction type %d",
272 insn->sec, insn->offset, insn->type);
273 return -1;
274 }
275
276 hash_add(file->insn_hash, &insn->hash, insn->offset);
277 list_add_tail(&insn->list, &file->insn_list);
278 }
279
280 list_for_each_entry(func, &sec->symbol_list, list) {
281 if (func->type != STT_FUNC)
282 continue;
283
284 if (!find_insn(file, sec, func->offset)) {
285 WARN("%s(): can't find starting instruction",
286 func->name);
287 return -1;
288 }
289
290 func_for_each_insn(file, func, insn)
291 if (!insn->func)
292 insn->func = func;
293 }
294 }
295
296 return 0;
297}
298
299/*
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500300 * Mark "ud2" instructions and manually annotated dead ends.
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500301 */
302static int add_dead_ends(struct objtool_file *file)
303{
304 struct section *sec;
305 struct rela *rela;
306 struct instruction *insn;
307 bool found;
308
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500309 /*
310 * By default, "ud2" is a dead end unless otherwise annotated, because
311 * GCC 7 inserts it for certain divide-by-zero cases.
312 */
313 for_each_insn(file, insn)
314 if (insn->type == INSN_BUG)
315 insn->dead_end = true;
316
317 /*
318 * Check for manually annotated dead ends.
319 */
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500320 sec = find_section_by_name(file->elf, ".rela.discard.unreachable");
321 if (!sec)
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500322 goto reachable;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500323
324 list_for_each_entry(rela, &sec->rela_list, list) {
325 if (rela->sym->type != STT_SECTION) {
326 WARN("unexpected relocation symbol type in %s", sec->name);
327 return -1;
328 }
329 insn = find_insn(file, rela->sym->sec, rela->addend);
330 if (insn)
331 insn = list_prev_entry(insn, list);
332 else if (rela->addend == rela->sym->sec->len) {
333 found = false;
334 list_for_each_entry_reverse(insn, &file->insn_list, list) {
335 if (insn->sec == rela->sym->sec) {
336 found = true;
337 break;
338 }
339 }
340
341 if (!found) {
342 WARN("can't find unreachable insn at %s+0x%x",
343 rela->sym->sec->name, rela->addend);
344 return -1;
345 }
346 } else {
347 WARN("can't find unreachable insn at %s+0x%x",
348 rela->sym->sec->name, rela->addend);
349 return -1;
350 }
351
352 insn->dead_end = true;
353 }
354
Josh Poimboeuf649ea4d2017-07-27 15:56:53 -0500355reachable:
356 /*
357 * These manually annotated reachable checks are needed for GCC 4.4,
358 * where the Linux unreachable() macro isn't supported. In that case
359 * GCC doesn't know the "ud2" is fatal, so it generates code as if it's
360 * not a dead end.
361 */
362 sec = find_section_by_name(file->elf, ".rela.discard.reachable");
363 if (!sec)
364 return 0;
365
366 list_for_each_entry(rela, &sec->rela_list, list) {
367 if (rela->sym->type != STT_SECTION) {
368 WARN("unexpected relocation symbol type in %s", sec->name);
369 return -1;
370 }
371 insn = find_insn(file, rela->sym->sec, rela->addend);
372 if (insn)
373 insn = list_prev_entry(insn, list);
374 else if (rela->addend == rela->sym->sec->len) {
375 found = false;
376 list_for_each_entry_reverse(insn, &file->insn_list, list) {
377 if (insn->sec == rela->sym->sec) {
378 found = true;
379 break;
380 }
381 }
382
383 if (!found) {
384 WARN("can't find reachable insn at %s+0x%x",
385 rela->sym->sec->name, rela->addend);
386 return -1;
387 }
388 } else {
389 WARN("can't find reachable insn at %s+0x%x",
390 rela->sym->sec->name, rela->addend);
391 return -1;
392 }
393
394 insn->dead_end = false;
395 }
396
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500397 return 0;
398}
399
400/*
401 * Warnings shouldn't be reported for ignored functions.
402 */
403static void add_ignores(struct objtool_file *file)
404{
405 struct instruction *insn;
406 struct section *sec;
407 struct symbol *func;
408
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500409 for_each_sec(file, sec) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500410 list_for_each_entry(func, &sec->symbol_list, list) {
411 if (func->type != STT_FUNC)
412 continue;
413
414 if (!ignore_func(file, func))
415 continue;
416
417 func_for_each_insn(file, func, insn)
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500418 insn->ignore = true;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500419 }
420 }
421}
422
423/*
424 * Find the destination instructions for all jumps.
425 */
426static int add_jump_destinations(struct objtool_file *file)
427{
428 struct instruction *insn;
429 struct rela *rela;
430 struct section *dest_sec;
431 unsigned long dest_off;
432
433 for_each_insn(file, insn) {
434 if (insn->type != INSN_JUMP_CONDITIONAL &&
435 insn->type != INSN_JUMP_UNCONDITIONAL)
436 continue;
437
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500438 if (insn->ignore)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500439 continue;
440
441 rela = find_rela_by_dest_range(insn->sec, insn->offset,
442 insn->len);
443 if (!rela) {
444 dest_sec = insn->sec;
445 dest_off = insn->offset + insn->len + insn->immediate;
446 } else if (rela->sym->type == STT_SECTION) {
447 dest_sec = rela->sym->sec;
448 dest_off = rela->addend + 4;
449 } else if (rela->sym->sec->idx) {
450 dest_sec = rela->sym->sec;
451 dest_off = rela->sym->sym.st_value + rela->addend + 4;
452 } else {
453 /* sibling call */
454 insn->jump_dest = 0;
455 continue;
456 }
457
458 insn->jump_dest = find_insn(file, dest_sec, dest_off);
459 if (!insn->jump_dest) {
460
461 /*
462 * This is a special case where an alt instruction
463 * jumps past the end of the section. These are
464 * handled later in handle_group_alt().
465 */
466 if (!strcmp(insn->sec->name, ".altinstr_replacement"))
467 continue;
468
469 WARN_FUNC("can't find jump dest instruction at %s+0x%lx",
470 insn->sec, insn->offset, dest_sec->name,
471 dest_off);
472 return -1;
473 }
474 }
475
476 return 0;
477}
478
479/*
480 * Find the destination instructions for all calls.
481 */
482static int add_call_destinations(struct objtool_file *file)
483{
484 struct instruction *insn;
485 unsigned long dest_off;
486 struct rela *rela;
487
488 for_each_insn(file, insn) {
489 if (insn->type != INSN_CALL)
490 continue;
491
492 rela = find_rela_by_dest_range(insn->sec, insn->offset,
493 insn->len);
494 if (!rela) {
495 dest_off = insn->offset + insn->len + insn->immediate;
496 insn->call_dest = find_symbol_by_offset(insn->sec,
497 dest_off);
498 if (!insn->call_dest) {
499 WARN_FUNC("can't find call dest symbol at offset 0x%lx",
500 insn->sec, insn->offset, dest_off);
501 return -1;
502 }
503 } else if (rela->sym->type == STT_SECTION) {
504 insn->call_dest = find_symbol_by_offset(rela->sym->sec,
505 rela->addend+4);
506 if (!insn->call_dest ||
507 insn->call_dest->type != STT_FUNC) {
508 WARN_FUNC("can't find call dest symbol at %s+0x%x",
509 insn->sec, insn->offset,
510 rela->sym->sec->name,
511 rela->addend + 4);
512 return -1;
513 }
514 } else
515 insn->call_dest = rela->sym;
516 }
517
518 return 0;
519}
520
521/*
522 * The .alternatives section requires some extra special care, over and above
523 * what other special sections require:
524 *
525 * 1. Because alternatives are patched in-place, we need to insert a fake jump
526 * instruction at the end so that validate_branch() skips all the original
527 * replaced instructions when validating the new instruction path.
528 *
529 * 2. An added wrinkle is that the new instruction length might be zero. In
530 * that case the old instructions are replaced with noops. We simulate that
531 * by creating a fake jump as the only new instruction.
532 *
533 * 3. In some cases, the alternative section includes an instruction which
534 * conditionally jumps to the _end_ of the entry. We have to modify these
535 * jumps' destinations to point back to .text rather than the end of the
536 * entry in .altinstr_replacement.
537 *
538 * 4. It has been requested that we don't validate the !POPCNT feature path
539 * which is a "very very small percentage of machines".
540 */
541static int handle_group_alt(struct objtool_file *file,
542 struct special_alt *special_alt,
543 struct instruction *orig_insn,
544 struct instruction **new_insn)
545{
546 struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump;
547 unsigned long dest_off;
548
549 last_orig_insn = NULL;
550 insn = orig_insn;
551 sec_for_each_insn_from(file, insn) {
552 if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
553 break;
554
555 if (special_alt->skip_orig)
556 insn->type = INSN_NOP;
557
558 insn->alt_group = true;
559 last_orig_insn = insn;
560 }
561
562 if (!next_insn_same_sec(file, last_orig_insn)) {
563 WARN("%s: don't know how to handle alternatives at end of section",
564 special_alt->orig_sec->name);
565 return -1;
566 }
567
568 fake_jump = malloc(sizeof(*fake_jump));
569 if (!fake_jump) {
570 WARN("malloc failed");
571 return -1;
572 }
573 memset(fake_jump, 0, sizeof(*fake_jump));
574 INIT_LIST_HEAD(&fake_jump->alts);
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500575 clear_insn_state(&fake_jump->state);
576
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500577 fake_jump->sec = special_alt->new_sec;
578 fake_jump->offset = -1;
579 fake_jump->type = INSN_JUMP_UNCONDITIONAL;
580 fake_jump->jump_dest = list_next_entry(last_orig_insn, list);
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500581 fake_jump->ignore = true;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500582
583 if (!special_alt->new_len) {
584 *new_insn = fake_jump;
585 return 0;
586 }
587
588 last_new_insn = NULL;
589 insn = *new_insn;
590 sec_for_each_insn_from(file, insn) {
591 if (insn->offset >= special_alt->new_off + special_alt->new_len)
592 break;
593
594 last_new_insn = insn;
595
596 if (insn->type != INSN_JUMP_CONDITIONAL &&
597 insn->type != INSN_JUMP_UNCONDITIONAL)
598 continue;
599
600 if (!insn->immediate)
601 continue;
602
603 dest_off = insn->offset + insn->len + insn->immediate;
604 if (dest_off == special_alt->new_off + special_alt->new_len)
605 insn->jump_dest = fake_jump;
606
607 if (!insn->jump_dest) {
608 WARN_FUNC("can't find alternative jump destination",
609 insn->sec, insn->offset);
610 return -1;
611 }
612 }
613
614 if (!last_new_insn) {
615 WARN_FUNC("can't find last new alternative instruction",
616 special_alt->new_sec, special_alt->new_off);
617 return -1;
618 }
619
620 list_add(&fake_jump->list, &last_new_insn->list);
621
622 return 0;
623}
624
625/*
626 * A jump table entry can either convert a nop to a jump or a jump to a nop.
627 * If the original instruction is a jump, make the alt entry an effective nop
628 * by just skipping the original instruction.
629 */
630static int handle_jump_alt(struct objtool_file *file,
631 struct special_alt *special_alt,
632 struct instruction *orig_insn,
633 struct instruction **new_insn)
634{
635 if (orig_insn->type == INSN_NOP)
636 return 0;
637
638 if (orig_insn->type != INSN_JUMP_UNCONDITIONAL) {
639 WARN_FUNC("unsupported instruction at jump label",
640 orig_insn->sec, orig_insn->offset);
641 return -1;
642 }
643
644 *new_insn = list_next_entry(orig_insn, list);
645 return 0;
646}
647
648/*
649 * Read all the special sections which have alternate instructions which can be
650 * patched in or redirected to at runtime. Each instruction having alternate
651 * instruction(s) has them added to its insn->alts list, which will be
652 * traversed in validate_branch().
653 */
654static int add_special_section_alts(struct objtool_file *file)
655{
656 struct list_head special_alts;
657 struct instruction *orig_insn, *new_insn;
658 struct special_alt *special_alt, *tmp;
659 struct alternative *alt;
660 int ret;
661
662 ret = special_get_alts(file->elf, &special_alts);
663 if (ret)
664 return ret;
665
666 list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {
667 alt = malloc(sizeof(*alt));
668 if (!alt) {
669 WARN("malloc failed");
670 ret = -1;
671 goto out;
672 }
673
674 orig_insn = find_insn(file, special_alt->orig_sec,
675 special_alt->orig_off);
676 if (!orig_insn) {
677 WARN_FUNC("special: can't find orig instruction",
678 special_alt->orig_sec, special_alt->orig_off);
679 ret = -1;
680 goto out;
681 }
682
683 new_insn = NULL;
684 if (!special_alt->group || special_alt->new_len) {
685 new_insn = find_insn(file, special_alt->new_sec,
686 special_alt->new_off);
687 if (!new_insn) {
688 WARN_FUNC("special: can't find new instruction",
689 special_alt->new_sec,
690 special_alt->new_off);
691 ret = -1;
692 goto out;
693 }
694 }
695
696 if (special_alt->group) {
697 ret = handle_group_alt(file, special_alt, orig_insn,
698 &new_insn);
699 if (ret)
700 goto out;
701 } else if (special_alt->jump_or_nop) {
702 ret = handle_jump_alt(file, special_alt, orig_insn,
703 &new_insn);
704 if (ret)
705 goto out;
706 }
707
708 alt->insn = new_insn;
709 list_add_tail(&alt->list, &orig_insn->alts);
710
711 list_del(&special_alt->list);
712 free(special_alt);
713 }
714
715out:
716 return ret;
717}
718
719static int add_switch_table(struct objtool_file *file, struct symbol *func,
720 struct instruction *insn, struct rela *table,
721 struct rela *next_table)
722{
723 struct rela *rela = table;
724 struct instruction *alt_insn;
725 struct alternative *alt;
726
727 list_for_each_entry_from(rela, &file->rodata->rela->rela_list, list) {
728 if (rela == next_table)
729 break;
730
731 if (rela->sym->sec != insn->sec ||
732 rela->addend <= func->offset ||
733 rela->addend >= func->offset + func->len)
734 break;
735
736 alt_insn = find_insn(file, insn->sec, rela->addend);
737 if (!alt_insn) {
738 WARN("%s: can't find instruction at %s+0x%x",
739 file->rodata->rela->name, insn->sec->name,
740 rela->addend);
741 return -1;
742 }
743
744 alt = malloc(sizeof(*alt));
745 if (!alt) {
746 WARN("malloc failed");
747 return -1;
748 }
749
750 alt->insn = alt_insn;
751 list_add_tail(&alt->list, &insn->alts);
752 }
753
754 return 0;
755}
756
757/*
758 * find_switch_table() - Given a dynamic jump, find the switch jump table in
759 * .rodata associated with it.
760 *
761 * There are 3 basic patterns:
762 *
763 * 1. jmpq *[rodata addr](,%reg,8)
764 *
765 * This is the most common case by far. It jumps to an address in a simple
766 * jump table which is stored in .rodata.
767 *
768 * 2. jmpq *[rodata addr](%rip)
769 *
770 * This is caused by a rare GCC quirk, currently only seen in three driver
771 * functions in the kernel, only with certain obscure non-distro configs.
772 *
773 * As part of an optimization, GCC makes a copy of an existing switch jump
774 * table, modifies it, and then hard-codes the jump (albeit with an indirect
775 * jump) to use a single entry in the table. The rest of the jump table and
776 * some of its jump targets remain as dead code.
777 *
778 * In such a case we can just crudely ignore all unreachable instruction
779 * warnings for the entire object file. Ideally we would just ignore them
780 * for the function, but that would require redesigning the code quite a
781 * bit. And honestly that's just not worth doing: unreachable instruction
782 * warnings are of questionable value anyway, and this is such a rare issue.
783 *
784 * 3. mov [rodata addr],%reg1
785 * ... some instructions ...
786 * jmpq *(%reg1,%reg2,8)
787 *
788 * This is a fairly uncommon pattern which is new for GCC 6. As of this
789 * writing, there are 11 occurrences of it in the allmodconfig kernel.
790 *
791 * TODO: Once we have DWARF CFI and smarter instruction decoding logic,
792 * ensure the same register is used in the mov and jump instructions.
793 */
794static struct rela *find_switch_table(struct objtool_file *file,
795 struct symbol *func,
796 struct instruction *insn)
797{
798 struct rela *text_rela, *rodata_rela;
799 struct instruction *orig_insn = insn;
800
801 text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len);
802 if (text_rela && text_rela->sym == file->rodata->sym) {
803 /* case 1 */
804 rodata_rela = find_rela_by_dest(file->rodata,
805 text_rela->addend);
806 if (rodata_rela)
807 return rodata_rela;
808
809 /* case 2 */
810 rodata_rela = find_rela_by_dest(file->rodata,
811 text_rela->addend + 4);
812 if (!rodata_rela)
813 return NULL;
814 file->ignore_unreachables = true;
815 return rodata_rela;
816 }
817
818 /* case 3 */
819 func_for_each_insn_continue_reverse(file, func, insn) {
820 if (insn->type == INSN_JUMP_DYNAMIC)
821 break;
822
823 /* allow small jumps within the range */
824 if (insn->type == INSN_JUMP_UNCONDITIONAL &&
825 insn->jump_dest &&
826 (insn->jump_dest->offset <= insn->offset ||
827 insn->jump_dest->offset > orig_insn->offset))
828 break;
829
830 /* look for a relocation which references .rodata */
831 text_rela = find_rela_by_dest_range(insn->sec, insn->offset,
832 insn->len);
833 if (!text_rela || text_rela->sym != file->rodata->sym)
834 continue;
835
836 /*
837 * Make sure the .rodata address isn't associated with a
838 * symbol. gcc jump tables are anonymous data.
839 */
840 if (find_symbol_containing(file->rodata, text_rela->addend))
841 continue;
842
843 return find_rela_by_dest(file->rodata, text_rela->addend);
844 }
845
846 return NULL;
847}
848
849static int add_func_switch_tables(struct objtool_file *file,
850 struct symbol *func)
851{
852 struct instruction *insn, *prev_jump = NULL;
853 struct rela *rela, *prev_rela = NULL;
854 int ret;
855
856 func_for_each_insn(file, func, insn) {
857 if (insn->type != INSN_JUMP_DYNAMIC)
858 continue;
859
860 rela = find_switch_table(file, func, insn);
861 if (!rela)
862 continue;
863
864 /*
865 * We found a switch table, but we don't know yet how big it
866 * is. Don't add it until we reach the end of the function or
867 * the beginning of another switch table in the same function.
868 */
869 if (prev_jump) {
870 ret = add_switch_table(file, func, prev_jump, prev_rela,
871 rela);
872 if (ret)
873 return ret;
874 }
875
876 prev_jump = insn;
877 prev_rela = rela;
878 }
879
880 if (prev_jump) {
881 ret = add_switch_table(file, func, prev_jump, prev_rela, NULL);
882 if (ret)
883 return ret;
884 }
885
886 return 0;
887}
888
889/*
890 * For some switch statements, gcc generates a jump table in the .rodata
891 * section which contains a list of addresses within the function to jump to.
892 * This finds these jump tables and adds them to the insn->alts lists.
893 */
894static int add_switch_table_alts(struct objtool_file *file)
895{
896 struct section *sec;
897 struct symbol *func;
898 int ret;
899
900 if (!file->rodata || !file->rodata->rela)
901 return 0;
902
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500903 for_each_sec(file, sec) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -0500904 list_for_each_entry(func, &sec->symbol_list, list) {
905 if (func->type != STT_FUNC)
906 continue;
907
908 ret = add_func_switch_tables(file, func);
909 if (ret)
910 return ret;
911 }
912 }
913
914 return 0;
915}
916
Josh Poimboeuf39358a02017-07-11 10:33:43 -0500917static int read_unwind_hints(struct objtool_file *file)
918{
919 struct section *sec, *relasec;
920 struct rela *rela;
921 struct unwind_hint *hint;
922 struct instruction *insn;
923 struct cfi_reg *cfa;
924 int i;
925
926 sec = find_section_by_name(file->elf, ".discard.unwind_hints");
927 if (!sec)
928 return 0;
929
930 relasec = sec->rela;
931 if (!relasec) {
932 WARN("missing .rela.discard.unwind_hints section");
933 return -1;
934 }
935
936 if (sec->len % sizeof(struct unwind_hint)) {
937 WARN("struct unwind_hint size mismatch");
938 return -1;
939 }
940
941 file->hints = true;
942
943 for (i = 0; i < sec->len / sizeof(struct unwind_hint); i++) {
944 hint = (struct unwind_hint *)sec->data->d_buf + i;
945
946 rela = find_rela_by_dest(sec, i * sizeof(*hint));
947 if (!rela) {
948 WARN("can't find rela for unwind_hints[%d]", i);
949 return -1;
950 }
951
952 insn = find_insn(file, rela->sym->sec, rela->addend);
953 if (!insn) {
954 WARN("can't find insn for unwind_hints[%d]", i);
955 return -1;
956 }
957
958 cfa = &insn->state.cfa;
959
960 if (hint->type == UNWIND_HINT_TYPE_SAVE) {
961 insn->save = true;
962 continue;
963
964 } else if (hint->type == UNWIND_HINT_TYPE_RESTORE) {
965 insn->restore = true;
966 insn->hint = true;
967 continue;
968 }
969
970 insn->hint = true;
971
972 switch (hint->sp_reg) {
973 case ORC_REG_UNDEFINED:
974 cfa->base = CFI_UNDEFINED;
975 break;
976 case ORC_REG_SP:
977 cfa->base = CFI_SP;
978 break;
979 case ORC_REG_BP:
980 cfa->base = CFI_BP;
981 break;
982 case ORC_REG_SP_INDIRECT:
983 cfa->base = CFI_SP_INDIRECT;
984 break;
985 case ORC_REG_R10:
986 cfa->base = CFI_R10;
987 break;
988 case ORC_REG_R13:
989 cfa->base = CFI_R13;
990 break;
991 case ORC_REG_DI:
992 cfa->base = CFI_DI;
993 break;
994 case ORC_REG_DX:
995 cfa->base = CFI_DX;
996 break;
997 default:
998 WARN_FUNC("unsupported unwind_hint sp base reg %d",
999 insn->sec, insn->offset, hint->sp_reg);
1000 return -1;
1001 }
1002
1003 cfa->offset = hint->sp_offset;
1004 insn->state.type = hint->type;
1005 }
1006
1007 return 0;
1008}
1009
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001010static int decode_sections(struct objtool_file *file)
1011{
1012 int ret;
1013
1014 ret = decode_instructions(file);
1015 if (ret)
1016 return ret;
1017
1018 ret = add_dead_ends(file);
1019 if (ret)
1020 return ret;
1021
1022 add_ignores(file);
1023
1024 ret = add_jump_destinations(file);
1025 if (ret)
1026 return ret;
1027
1028 ret = add_call_destinations(file);
1029 if (ret)
1030 return ret;
1031
1032 ret = add_special_section_alts(file);
1033 if (ret)
1034 return ret;
1035
1036 ret = add_switch_table_alts(file);
1037 if (ret)
1038 return ret;
1039
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001040 ret = read_unwind_hints(file);
1041 if (ret)
1042 return ret;
1043
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001044 return 0;
1045}
1046
1047static bool is_fentry_call(struct instruction *insn)
1048{
1049 if (insn->type == INSN_CALL &&
1050 insn->call_dest->type == STT_NOTYPE &&
1051 !strcmp(insn->call_dest->name, "__fentry__"))
1052 return true;
1053
1054 return false;
1055}
1056
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001057static bool has_modified_stack_frame(struct insn_state *state)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001058{
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001059 int i;
1060
1061 if (state->cfa.base != initial_func_cfi.cfa.base ||
1062 state->cfa.offset != initial_func_cfi.cfa.offset ||
1063 state->stack_size != initial_func_cfi.cfa.offset ||
1064 state->drap)
1065 return true;
1066
1067 for (i = 0; i < CFI_NUM_REGS; i++)
1068 if (state->regs[i].base != initial_func_cfi.regs[i].base ||
1069 state->regs[i].offset != initial_func_cfi.regs[i].offset)
1070 return true;
1071
1072 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001073}
1074
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001075static bool has_valid_stack_frame(struct insn_state *state)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001076{
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001077 if (state->cfa.base == CFI_BP && state->regs[CFI_BP].base == CFI_CFA &&
1078 state->regs[CFI_BP].offset == -16)
1079 return true;
1080
1081 if (state->drap && state->regs[CFI_BP].base == CFI_BP)
1082 return true;
1083
1084 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001085}
1086
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001087static int update_insn_state_regs(struct instruction *insn, struct insn_state *state)
1088{
1089 struct cfi_reg *cfa = &state->cfa;
1090 struct stack_op *op = &insn->stack_op;
1091
1092 if (cfa->base != CFI_SP)
1093 return 0;
1094
1095 /* push */
1096 if (op->dest.type == OP_DEST_PUSH)
1097 cfa->offset += 8;
1098
1099 /* pop */
1100 if (op->src.type == OP_SRC_POP)
1101 cfa->offset -= 8;
1102
1103 /* add immediate to sp */
1104 if (op->dest.type == OP_DEST_REG && op->src.type == OP_SRC_ADD &&
1105 op->dest.reg == CFI_SP && op->src.reg == CFI_SP)
1106 cfa->offset -= op->src.offset;
1107
1108 return 0;
1109}
1110
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001111static void save_reg(struct insn_state *state, unsigned char reg, int base,
1112 int offset)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001113{
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001114 if (arch_callee_saved_reg(reg) &&
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001115 state->regs[reg].base == CFI_UNDEFINED) {
1116 state->regs[reg].base = base;
1117 state->regs[reg].offset = offset;
1118 }
1119}
1120
1121static void restore_reg(struct insn_state *state, unsigned char reg)
1122{
1123 state->regs[reg].base = CFI_UNDEFINED;
1124 state->regs[reg].offset = 0;
1125}
1126
1127/*
1128 * A note about DRAP stack alignment:
1129 *
1130 * GCC has the concept of a DRAP register, which is used to help keep track of
1131 * the stack pointer when aligning the stack. r10 or r13 is used as the DRAP
1132 * register. The typical DRAP pattern is:
1133 *
1134 * 4c 8d 54 24 08 lea 0x8(%rsp),%r10
1135 * 48 83 e4 c0 and $0xffffffffffffffc0,%rsp
1136 * 41 ff 72 f8 pushq -0x8(%r10)
1137 * 55 push %rbp
1138 * 48 89 e5 mov %rsp,%rbp
1139 * (more pushes)
1140 * 41 52 push %r10
1141 * ...
1142 * 41 5a pop %r10
1143 * (more pops)
1144 * 5d pop %rbp
1145 * 49 8d 62 f8 lea -0x8(%r10),%rsp
1146 * c3 retq
1147 *
1148 * There are some variations in the epilogues, like:
1149 *
1150 * 5b pop %rbx
1151 * 41 5a pop %r10
1152 * 41 5c pop %r12
1153 * 41 5d pop %r13
1154 * 41 5e pop %r14
1155 * c9 leaveq
1156 * 49 8d 62 f8 lea -0x8(%r10),%rsp
1157 * c3 retq
1158 *
1159 * and:
1160 *
1161 * 4c 8b 55 e8 mov -0x18(%rbp),%r10
1162 * 48 8b 5d e0 mov -0x20(%rbp),%rbx
1163 * 4c 8b 65 f0 mov -0x10(%rbp),%r12
1164 * 4c 8b 6d f8 mov -0x8(%rbp),%r13
1165 * c9 leaveq
1166 * 49 8d 62 f8 lea -0x8(%r10),%rsp
1167 * c3 retq
1168 *
1169 * Sometimes r13 is used as the DRAP register, in which case it's saved and
1170 * restored beforehand:
1171 *
1172 * 41 55 push %r13
1173 * 4c 8d 6c 24 10 lea 0x10(%rsp),%r13
1174 * 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1175 * ...
1176 * 49 8d 65 f0 lea -0x10(%r13),%rsp
1177 * 41 5d pop %r13
1178 * c3 retq
1179 */
1180static int update_insn_state(struct instruction *insn, struct insn_state *state)
1181{
1182 struct stack_op *op = &insn->stack_op;
1183 struct cfi_reg *cfa = &state->cfa;
1184 struct cfi_reg *regs = state->regs;
1185
1186 /* stack operations don't make sense with an undefined CFA */
1187 if (cfa->base == CFI_UNDEFINED) {
1188 if (insn->func) {
1189 WARN_FUNC("undefined stack state", insn->sec, insn->offset);
1190 return -1;
1191 }
1192 return 0;
1193 }
1194
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001195 if (state->type == ORC_TYPE_REGS || state->type == ORC_TYPE_REGS_IRET)
1196 return update_insn_state_regs(insn, state);
1197
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001198 switch (op->dest.type) {
1199
1200 case OP_DEST_REG:
1201 switch (op->src.type) {
1202
1203 case OP_SRC_REG:
1204 if (cfa->base == op->src.reg && cfa->base == CFI_SP &&
1205 op->dest.reg == CFI_BP && regs[CFI_BP].base == CFI_CFA &&
1206 regs[CFI_BP].offset == -cfa->offset) {
1207
1208 /* mov %rsp, %rbp */
1209 cfa->base = op->dest.reg;
1210 state->bp_scratch = false;
1211 } else if (state->drap) {
1212
1213 /* drap: mov %rsp, %rbp */
1214 regs[CFI_BP].base = CFI_BP;
1215 regs[CFI_BP].offset = -state->stack_size;
1216 state->bp_scratch = false;
Josh Poimboeuf867ac9d2017-07-24 18:34:14 -05001217 } else if (!no_fp) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001218
1219 WARN_FUNC("unknown stack-related register move",
1220 insn->sec, insn->offset);
1221 return -1;
1222 }
1223
1224 break;
1225
1226 case OP_SRC_ADD:
1227 if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) {
1228
1229 /* add imm, %rsp */
1230 state->stack_size -= op->src.offset;
1231 if (cfa->base == CFI_SP)
1232 cfa->offset -= op->src.offset;
1233 break;
1234 }
1235
1236 if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) {
1237
1238 /* lea disp(%rbp), %rsp */
1239 state->stack_size = -(op->src.offset + regs[CFI_BP].offset);
1240 break;
1241 }
1242
1243 if (op->dest.reg != CFI_BP && op->src.reg == CFI_SP &&
1244 cfa->base == CFI_SP) {
1245
1246 /* drap: lea disp(%rsp), %drap */
1247 state->drap_reg = op->dest.reg;
1248 break;
1249 }
1250
1251 if (state->drap && op->dest.reg == CFI_SP &&
1252 op->src.reg == state->drap_reg) {
1253
1254 /* drap: lea disp(%drap), %rsp */
1255 cfa->base = CFI_SP;
1256 cfa->offset = state->stack_size = -op->src.offset;
1257 state->drap_reg = CFI_UNDEFINED;
1258 state->drap = false;
1259 break;
1260 }
1261
1262 if (op->dest.reg == state->cfa.base) {
1263 WARN_FUNC("unsupported stack register modification",
1264 insn->sec, insn->offset);
1265 return -1;
1266 }
1267
1268 break;
1269
1270 case OP_SRC_AND:
1271 if (op->dest.reg != CFI_SP ||
1272 (state->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
1273 (state->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
1274 WARN_FUNC("unsupported stack pointer realignment",
1275 insn->sec, insn->offset);
1276 return -1;
1277 }
1278
1279 if (state->drap_reg != CFI_UNDEFINED) {
1280 /* drap: and imm, %rsp */
1281 cfa->base = state->drap_reg;
1282 cfa->offset = state->stack_size = 0;
1283 state->drap = true;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001284 }
1285
1286 /*
1287 * Older versions of GCC (4.8ish) realign the stack
1288 * without DRAP, with a frame pointer.
1289 */
1290
1291 break;
1292
1293 case OP_SRC_POP:
1294 if (!state->drap && op->dest.type == OP_DEST_REG &&
1295 op->dest.reg == cfa->base) {
1296
1297 /* pop %rbp */
1298 cfa->base = CFI_SP;
1299 }
1300
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001301 if (state->drap && cfa->base == CFI_BP_INDIRECT &&
1302 op->dest.type == OP_DEST_REG &&
1303 op->dest.reg == state->drap_reg &&
1304 state->drap_offset == -state->stack_size) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001305
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001306 /* drap: pop %drap */
1307 cfa->base = state->drap_reg;
1308 cfa->offset = 0;
1309 state->drap_offset = -1;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001310
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001311 } else if (regs[op->dest.reg].offset == -state->stack_size) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001312
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001313 /* pop %reg */
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001314 restore_reg(state, op->dest.reg);
1315 }
1316
1317 state->stack_size -= 8;
1318 if (cfa->base == CFI_SP)
1319 cfa->offset -= 8;
1320
1321 break;
1322
1323 case OP_SRC_REG_INDIRECT:
1324 if (state->drap && op->src.reg == CFI_BP &&
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001325 op->src.offset == state->drap_offset) {
1326
1327 /* drap: mov disp(%rbp), %drap */
1328 cfa->base = state->drap_reg;
1329 cfa->offset = 0;
1330 state->drap_offset = -1;
1331 }
1332
1333 if (state->drap && op->src.reg == CFI_BP &&
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001334 op->src.offset == regs[op->dest.reg].offset) {
1335
1336 /* drap: mov disp(%rbp), %reg */
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001337 restore_reg(state, op->dest.reg);
1338
1339 } else if (op->src.reg == cfa->base &&
1340 op->src.offset == regs[op->dest.reg].offset + cfa->offset) {
1341
1342 /* mov disp(%rbp), %reg */
1343 /* mov disp(%rsp), %reg */
1344 restore_reg(state, op->dest.reg);
1345 }
1346
1347 break;
1348
1349 default:
1350 WARN_FUNC("unknown stack-related instruction",
1351 insn->sec, insn->offset);
1352 return -1;
1353 }
1354
1355 break;
1356
1357 case OP_DEST_PUSH:
1358 state->stack_size += 8;
1359 if (cfa->base == CFI_SP)
1360 cfa->offset += 8;
1361
1362 if (op->src.type != OP_SRC_REG)
1363 break;
1364
1365 if (state->drap) {
1366 if (op->src.reg == cfa->base && op->src.reg == state->drap_reg) {
1367
1368 /* drap: push %drap */
1369 cfa->base = CFI_BP_INDIRECT;
1370 cfa->offset = -state->stack_size;
1371
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001372 /* save drap so we know when to restore it */
1373 state->drap_offset = -state->stack_size;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001374
1375 } else if (op->src.reg == CFI_BP && cfa->base == state->drap_reg) {
1376
1377 /* drap: push %rbp */
1378 state->stack_size = 0;
1379
1380 } else if (regs[op->src.reg].base == CFI_UNDEFINED) {
1381
1382 /* drap: push %reg */
1383 save_reg(state, op->src.reg, CFI_BP, -state->stack_size);
1384 }
1385
1386 } else {
1387
1388 /* push %reg */
1389 save_reg(state, op->src.reg, CFI_CFA, -state->stack_size);
1390 }
1391
1392 /* detect when asm code uses rbp as a scratch register */
Josh Poimboeuf867ac9d2017-07-24 18:34:14 -05001393 if (!no_fp && insn->func && op->src.reg == CFI_BP &&
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001394 cfa->base != CFI_BP)
1395 state->bp_scratch = true;
1396 break;
1397
1398 case OP_DEST_REG_INDIRECT:
1399
1400 if (state->drap) {
1401 if (op->src.reg == cfa->base && op->src.reg == state->drap_reg) {
1402
1403 /* drap: mov %drap, disp(%rbp) */
1404 cfa->base = CFI_BP_INDIRECT;
1405 cfa->offset = op->dest.offset;
1406
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001407 /* save drap offset so we know when to restore it */
1408 state->drap_offset = op->dest.offset;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001409 }
1410
1411 else if (regs[op->src.reg].base == CFI_UNDEFINED) {
1412
1413 /* drap: mov reg, disp(%rbp) */
1414 save_reg(state, op->src.reg, CFI_BP, op->dest.offset);
1415 }
1416
1417 } else if (op->dest.reg == cfa->base) {
1418
1419 /* mov reg, disp(%rbp) */
1420 /* mov reg, disp(%rsp) */
1421 save_reg(state, op->src.reg, CFI_CFA,
1422 op->dest.offset - state->cfa.offset);
1423 }
1424
1425 break;
1426
1427 case OP_DEST_LEAVE:
1428 if ((!state->drap && cfa->base != CFI_BP) ||
1429 (state->drap && cfa->base != state->drap_reg)) {
1430 WARN_FUNC("leave instruction with modified stack frame",
1431 insn->sec, insn->offset);
1432 return -1;
1433 }
1434
1435 /* leave (mov %rbp, %rsp; pop %rbp) */
1436
1437 state->stack_size = -state->regs[CFI_BP].offset - 8;
1438 restore_reg(state, CFI_BP);
1439
1440 if (!state->drap) {
1441 cfa->base = CFI_SP;
1442 cfa->offset -= 8;
1443 }
1444
1445 break;
1446
1447 case OP_DEST_MEM:
1448 if (op->src.type != OP_SRC_POP) {
1449 WARN_FUNC("unknown stack-related memory operation",
1450 insn->sec, insn->offset);
1451 return -1;
1452 }
1453
1454 /* pop mem */
1455 state->stack_size -= 8;
1456 if (cfa->base == CFI_SP)
1457 cfa->offset -= 8;
1458
1459 break;
1460
1461 default:
1462 WARN_FUNC("unknown stack-related instruction",
1463 insn->sec, insn->offset);
1464 return -1;
1465 }
1466
1467 return 0;
1468}
1469
1470static bool insn_state_match(struct instruction *insn, struct insn_state *state)
1471{
1472 struct insn_state *state1 = &insn->state, *state2 = state;
1473 int i;
1474
1475 if (memcmp(&state1->cfa, &state2->cfa, sizeof(state1->cfa))) {
1476 WARN_FUNC("stack state mismatch: cfa1=%d%+d cfa2=%d%+d",
1477 insn->sec, insn->offset,
1478 state1->cfa.base, state1->cfa.offset,
1479 state2->cfa.base, state2->cfa.offset);
1480
1481 } else if (memcmp(&state1->regs, &state2->regs, sizeof(state1->regs))) {
1482 for (i = 0; i < CFI_NUM_REGS; i++) {
1483 if (!memcmp(&state1->regs[i], &state2->regs[i],
1484 sizeof(struct cfi_reg)))
1485 continue;
1486
1487 WARN_FUNC("stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d",
1488 insn->sec, insn->offset,
1489 i, state1->regs[i].base, state1->regs[i].offset,
1490 i, state2->regs[i].base, state2->regs[i].offset);
1491 break;
1492 }
1493
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001494 } else if (state1->type != state2->type) {
1495 WARN_FUNC("stack state mismatch: type1=%d type2=%d",
1496 insn->sec, insn->offset, state1->type, state2->type);
1497
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001498 } else if (state1->drap != state2->drap ||
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001499 (state1->drap && state1->drap_reg != state2->drap_reg) ||
1500 (state1->drap && state1->drap_offset != state2->drap_offset)) {
1501 WARN_FUNC("stack state mismatch: drap1=%d(%d,%d) drap2=%d(%d,%d)",
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001502 insn->sec, insn->offset,
Josh Poimboeufbf4d1a82017-08-10 16:37:26 -05001503 state1->drap, state1->drap_reg, state1->drap_offset,
1504 state2->drap, state2->drap_reg, state2->drap_offset);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001505
1506 } else
1507 return true;
1508
1509 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001510}
1511
1512/*
1513 * Follow the branch starting at the given instruction, and recursively follow
1514 * any other branches (jumps). Meanwhile, track the frame pointer state at
1515 * each instruction and validate all the rules described in
1516 * tools/objtool/Documentation/stack-validation.txt.
1517 */
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001518static int validate_branch(struct objtool_file *file, struct instruction *first,
1519 struct insn_state state)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001520{
1521 struct alternative *alt;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001522 struct instruction *insn, *next_insn;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001523 struct section *sec;
1524 struct symbol *func = NULL;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001525 int ret;
1526
1527 insn = first;
1528 sec = insn->sec;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001529
1530 if (insn->alt_group && list_empty(&insn->alts)) {
1531 WARN_FUNC("don't know how to handle branch to middle of alternative instruction group",
1532 sec, insn->offset);
Josh Poimboeuf12b25722017-08-10 16:37:25 -05001533 return 1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001534 }
1535
1536 while (1) {
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001537 next_insn = next_insn_same_sec(file, insn);
1538
Josh Poimboeufee976382017-08-11 12:24:15 -05001539
1540 if (file->c_file && func && insn->func && func != insn->func) {
1541 WARN("%s() falls through to next function %s()",
1542 func->name, insn->func->name);
1543 return 1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001544 }
1545
Josh Poimboeufee976382017-08-11 12:24:15 -05001546 if (insn->func)
1547 func = insn->func;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001548
Josh Poimboeuf48550222017-07-07 09:19:42 -05001549 if (func && insn->ignore) {
1550 WARN_FUNC("BUG: why am I validating an ignored function?",
1551 sec, insn->offset);
Josh Poimboeuf12b25722017-08-10 16:37:25 -05001552 return 1;
Josh Poimboeuf48550222017-07-07 09:19:42 -05001553 }
1554
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001555 if (insn->visited) {
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001556 if (!insn->hint && !insn_state_match(insn, &state))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001557 return 1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001558
1559 return 0;
1560 }
1561
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001562 if (insn->hint) {
1563 if (insn->restore) {
1564 struct instruction *save_insn, *i;
1565
1566 i = insn;
1567 save_insn = NULL;
1568 func_for_each_insn_continue_reverse(file, func, i) {
1569 if (i->save) {
1570 save_insn = i;
1571 break;
1572 }
1573 }
1574
1575 if (!save_insn) {
1576 WARN_FUNC("no corresponding CFI save for CFI restore",
1577 sec, insn->offset);
1578 return 1;
1579 }
1580
1581 if (!save_insn->visited) {
1582 /*
1583 * Oops, no state to copy yet.
1584 * Hopefully we can reach this
1585 * instruction from another branch
1586 * after the save insn has been
1587 * visited.
1588 */
1589 if (insn == first)
1590 return 0;
1591
1592 WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
1593 sec, insn->offset);
1594 return 1;
1595 }
1596
1597 insn->state = save_insn->state;
1598 }
1599
1600 state = insn->state;
1601
1602 } else
1603 insn->state = state;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001604
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001605 insn->visited = true;
1606
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001607 list_for_each_entry(alt, &insn->alts, list) {
1608 ret = validate_branch(file, alt->insn, state);
1609 if (ret)
1610 return 1;
1611 }
1612
1613 switch (insn->type) {
1614
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001615 case INSN_RETURN:
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001616 if (func && has_modified_stack_frame(&state)) {
1617 WARN_FUNC("return with modified stack frame",
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001618 sec, insn->offset);
1619 return 1;
1620 }
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001621
1622 if (state.bp_scratch) {
1623 WARN("%s uses BP as a scratch register",
1624 insn->func->name);
1625 return 1;
1626 }
1627
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001628 return 0;
1629
1630 case INSN_CALL:
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001631 if (is_fentry_call(insn))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001632 break;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001633
1634 ret = dead_end_function(file, insn->call_dest);
1635 if (ret == 1)
1636 return 0;
1637 if (ret == -1)
1638 return 1;
1639
1640 /* fallthrough */
1641 case INSN_CALL_DYNAMIC:
Josh Poimboeuf867ac9d2017-07-24 18:34:14 -05001642 if (!no_fp && func && !has_valid_stack_frame(&state)) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001643 WARN_FUNC("call without frame pointer save/setup",
1644 sec, insn->offset);
1645 return 1;
1646 }
1647 break;
1648
1649 case INSN_JUMP_CONDITIONAL:
1650 case INSN_JUMP_UNCONDITIONAL:
Josh Poimboeuf48550222017-07-07 09:19:42 -05001651 if (insn->jump_dest &&
1652 (!func || !insn->jump_dest->func ||
1653 func == insn->jump_dest->func)) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001654 ret = validate_branch(file, insn->jump_dest,
1655 state);
1656 if (ret)
1657 return 1;
Josh Poimboeuf48550222017-07-07 09:19:42 -05001658
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001659 } else if (func && has_modified_stack_frame(&state)) {
1660 WARN_FUNC("sibling call from callable instruction with modified stack frame",
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001661 sec, insn->offset);
1662 return 1;
Josh Poimboeuf48550222017-07-07 09:19:42 -05001663 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001664
1665 if (insn->type == INSN_JUMP_UNCONDITIONAL)
1666 return 0;
1667
1668 break;
1669
1670 case INSN_JUMP_DYNAMIC:
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001671 if (func && list_empty(&insn->alts) &&
1672 has_modified_stack_frame(&state)) {
1673 WARN_FUNC("sibling call from callable instruction with modified stack frame",
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001674 sec, insn->offset);
1675 return 1;
1676 }
1677
1678 return 0;
1679
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001680 case INSN_CONTEXT_SWITCH:
1681 if (func && (!next_insn || !next_insn->hint)) {
1682 WARN_FUNC("unsupported instruction in callable function",
1683 sec, insn->offset);
1684 return 1;
1685 }
1686 return 0;
1687
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001688 case INSN_STACK:
1689 if (update_insn_state(insn, &state))
Josh Poimboeuf12b25722017-08-10 16:37:25 -05001690 return 1;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001691
1692 break;
1693
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001694 default:
1695 break;
1696 }
1697
1698 if (insn->dead_end)
1699 return 0;
1700
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001701 insn = next_insn;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001702 if (!insn) {
1703 WARN("%s: unexpected end of section", sec->name);
1704 return 1;
1705 }
1706 }
1707
1708 return 0;
1709}
1710
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001711static int validate_unwind_hints(struct objtool_file *file)
1712{
1713 struct instruction *insn;
1714 int ret, warnings = 0;
1715 struct insn_state state;
1716
1717 if (!file->hints)
1718 return 0;
1719
1720 clear_insn_state(&state);
1721
1722 for_each_insn(file, insn) {
1723 if (insn->hint && !insn->visited) {
1724 ret = validate_branch(file, insn, state);
1725 warnings += ret;
1726 }
1727 }
1728
1729 return warnings;
1730}
1731
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001732static bool is_kasan_insn(struct instruction *insn)
1733{
1734 return (insn->type == INSN_CALL &&
1735 !strcmp(insn->call_dest->name, "__asan_handle_no_return"));
1736}
1737
1738static bool is_ubsan_insn(struct instruction *insn)
1739{
1740 return (insn->type == INSN_CALL &&
1741 !strcmp(insn->call_dest->name,
1742 "__ubsan_handle_builtin_unreachable"));
1743}
1744
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001745static bool ignore_unreachable_insn(struct instruction *insn)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001746{
1747 int i;
1748
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001749 if (insn->ignore || insn->type == INSN_NOP)
1750 return true;
1751
1752 /*
1753 * Ignore any unused exceptions. This can happen when a whitelisted
1754 * function has an exception table entry.
Josh Poimboeuf0e2bb2b2017-07-27 15:56:54 -05001755 *
1756 * Also ignore alternative replacement instructions. This can happen
1757 * when a whitelisted function uses one of the ALTERNATIVE macros.
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001758 */
Josh Poimboeuf0e2bb2b2017-07-27 15:56:54 -05001759 if (!strcmp(insn->sec->name, ".fixup") ||
1760 !strcmp(insn->sec->name, ".altinstr_replacement") ||
1761 !strcmp(insn->sec->name, ".altinstr_aux"))
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001762 return true;
1763
1764 /*
1765 * Check if this (or a subsequent) instruction is related to
1766 * CONFIG_UBSAN or CONFIG_KASAN.
1767 *
1768 * End the search at 5 instructions to avoid going into the weeds.
1769 */
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001770 if (!insn->func)
1771 return false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001772 for (i = 0; i < 5; i++) {
1773
1774 if (is_kasan_insn(insn) || is_ubsan_insn(insn))
1775 return true;
1776
1777 if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest) {
1778 insn = insn->jump_dest;
1779 continue;
1780 }
1781
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001782 if (insn->offset + insn->len >= insn->func->offset + insn->func->len)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001783 break;
1784 insn = list_next_entry(insn, list);
1785 }
1786
1787 return false;
1788}
1789
1790static int validate_functions(struct objtool_file *file)
1791{
1792 struct section *sec;
1793 struct symbol *func;
1794 struct instruction *insn;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001795 struct insn_state state;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001796 int ret, warnings = 0;
1797
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001798 clear_insn_state(&state);
1799
1800 state.cfa = initial_func_cfi.cfa;
1801 memcpy(&state.regs, &initial_func_cfi.regs,
1802 CFI_NUM_REGS * sizeof(struct cfi_reg));
1803 state.stack_size = initial_func_cfi.cfa.offset;
1804
1805 for_each_sec(file, sec) {
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001806 list_for_each_entry(func, &sec->symbol_list, list) {
1807 if (func->type != STT_FUNC)
1808 continue;
1809
1810 insn = find_insn(file, sec, func->offset);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001811 if (!insn || insn->ignore)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001812 continue;
1813
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001814 ret = validate_branch(file, insn, state);
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001815 warnings += ret;
1816 }
1817 }
1818
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001819 return warnings;
1820}
1821
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001822static int validate_reachable_instructions(struct objtool_file *file)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001823{
1824 struct instruction *insn;
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001825
1826 if (file->ignore_unreachables)
1827 return 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001828
1829 for_each_insn(file, insn) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001830 if (insn->visited || ignore_unreachable_insn(insn))
1831 continue;
1832
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001833 WARN_FUNC("unreachable instruction", insn->sec, insn->offset);
1834 return 1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001835 }
1836
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001837 return 0;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001838}
1839
1840static void cleanup(struct objtool_file *file)
1841{
1842 struct instruction *insn, *tmpinsn;
1843 struct alternative *alt, *tmpalt;
1844
1845 list_for_each_entry_safe(insn, tmpinsn, &file->insn_list, list) {
1846 list_for_each_entry_safe(alt, tmpalt, &insn->alts, list) {
1847 list_del(&alt->list);
1848 free(alt);
1849 }
1850 list_del(&insn->list);
1851 hash_del(&insn->hash);
1852 free(insn);
1853 }
1854 elf_close(file->elf);
1855}
1856
Josh Poimboeuf867ac9d2017-07-24 18:34:14 -05001857int check(const char *_objname, bool _no_fp, bool no_unreachable, bool orc)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001858{
1859 struct objtool_file file;
1860 int ret, warnings = 0;
1861
1862 objname = _objname;
Josh Poimboeuf867ac9d2017-07-24 18:34:14 -05001863 no_fp = _no_fp;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001864
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001865 file.elf = elf_open(objname, orc ? O_RDWR : O_RDONLY);
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001866 if (!file.elf)
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001867 return 1;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001868
1869 INIT_LIST_HEAD(&file.insn_list);
1870 hash_init(file.insn_hash);
1871 file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard");
1872 file.rodata = find_section_by_name(file.elf, ".rodata");
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001873 file.c_file = find_section_by_name(file.elf, ".comment");
Josh Poimboeuf867ac9d2017-07-24 18:34:14 -05001874 file.ignore_unreachables = no_unreachable;
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001875 file.hints = false;
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001876
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001877 arch_initial_func_cfi_state(&initial_func_cfi);
1878
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001879 ret = decode_sections(&file);
1880 if (ret < 0)
1881 goto out;
1882 warnings += ret;
1883
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001884 if (list_empty(&file.insn_list))
1885 goto out;
1886
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001887 ret = validate_functions(&file);
1888 if (ret < 0)
1889 goto out;
1890 warnings += ret;
1891
Josh Poimboeuf39358a02017-07-11 10:33:43 -05001892 ret = validate_unwind_hints(&file);
1893 if (ret < 0)
1894 goto out;
1895 warnings += ret;
1896
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001897 if (!warnings) {
1898 ret = validate_reachable_instructions(&file);
1899 if (ret < 0)
1900 goto out;
1901 warnings += ret;
1902 }
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001903
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001904 if (orc) {
1905 ret = create_orc(&file);
1906 if (ret < 0)
1907 goto out;
1908
1909 ret = create_orc_sections(&file);
1910 if (ret < 0)
1911 goto out;
1912
1913 ret = elf_write(file.elf);
1914 if (ret < 0)
1915 goto out;
1916 }
1917
Josh Poimboeufdcc914f2017-06-28 10:11:05 -05001918out:
1919 cleanup(&file);
1920
1921 /* ignore warnings for now until we get all the code cleaned up */
1922 if (ret || warnings)
1923 return 0;
1924 return 0;
1925}