objtool: Add tool to perform compile-time stack metadata validation
[linux-2.6-block.git] / tools / objtool / builtin-check.c
CommitLineData
442f04c3
JP
1/*
2 * Copyright (C) 2015 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/*
19 * objtool check:
20 *
21 * This command analyzes every .o file and ensures the validity of its stack
22 * trace metadata. It enforces a set of rules on asm code and C inline
23 * assembly code so that stack traces can be reliable.
24 *
25 * For more information, see tools/objtool/Documentation/stack-validation.txt.
26 */
27
28#include <string.h>
29#include <subcmd/parse-options.h>
30
31#include "builtin.h"
32#include "elf.h"
33#include "special.h"
34#include "arch.h"
35#include "warn.h"
36
37#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
38
39#define STATE_FP_SAVED 0x1
40#define STATE_FP_SETUP 0x2
41#define STATE_FENTRY 0x4
42
43struct instruction {
44 struct list_head list;
45 struct section *sec;
46 unsigned long offset;
47 unsigned int len, state;
48 unsigned char type;
49 unsigned long immediate;
50 bool alt_group, visited;
51 struct symbol *call_dest;
52 struct instruction *jump_dest;
53 struct list_head alts;
54};
55
56struct alternative {
57 struct list_head list;
58 struct instruction *insn;
59};
60
61struct objtool_file {
62 struct elf *elf;
63 struct list_head insns;
64};
65
66const char *objname;
67static bool nofp;
68
69static struct instruction *find_instruction(struct objtool_file *file,
70 struct section *sec,
71 unsigned long offset)
72{
73 struct instruction *insn;
74
75 list_for_each_entry(insn, &file->insns, list)
76 if (insn->sec == sec && insn->offset == offset)
77 return insn;
78
79 return NULL;
80}
81
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 section *macro_sec;
90 struct rela *rela;
91 struct instruction *insn;
92
93 /* check for STACK_FRAME_NON_STANDARD */
94 macro_sec = find_section_by_name(file->elf, "__func_stack_frame_non_standard");
95 if (macro_sec && macro_sec->rela)
96 list_for_each_entry(rela, &macro_sec->rela->relas, list)
97 if (rela->sym->sec == func->sec &&
98 rela->addend == func->offset)
99 return true;
100
101 /* check if it has a context switching instruction */
102 insn = find_instruction(file, func->sec, func->offset);
103 if (!insn)
104 return false;
105 list_for_each_entry_from(insn, &file->insns, list) {
106 if (insn->sec != func->sec ||
107 insn->offset >= func->offset + func->len)
108 break;
109 if (insn->type == INSN_CONTEXT_SWITCH)
110 return true;
111 }
112
113 return false;
114}
115
116/*
117 * This checks to see if the given function is a "noreturn" function.
118 *
119 * For global functions which are outside the scope of this object file, we
120 * have to keep a manual list of them.
121 *
122 * For local functions, we have to detect them manually by simply looking for
123 * the lack of a return instruction.
124 */
125static bool dead_end_function(struct objtool_file *file, struct symbol *func)
126{
127 int i;
128 struct instruction *insn;
129 bool empty = true;
130
131 /*
132 * Unfortunately these have to be hard coded because the noreturn
133 * attribute isn't provided in ELF data.
134 */
135 static const char * const global_noreturns[] = {
136 "__stack_chk_fail",
137 "panic",
138 "do_exit",
139 "__module_put_and_exit",
140 "complete_and_exit",
141 "kvm_spurious_fault",
142 "__reiserfs_panic",
143 "lbug_with_loc"
144 };
145
146 if (func->bind == STB_WEAK)
147 return false;
148
149 if (func->bind == STB_GLOBAL)
150 for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
151 if (!strcmp(func->name, global_noreturns[i]))
152 return true;
153
154 if (!func->sec)
155 return false;
156
157 insn = find_instruction(file, func->sec, func->offset);
158 if (!insn)
159 return false;
160
161 list_for_each_entry_from(insn, &file->insns, list) {
162 if (insn->sec != func->sec ||
163 insn->offset >= func->offset + func->len)
164 break;
165
166 empty = false;
167
168 if (insn->type == INSN_RETURN)
169 return false;
170
171 if (insn->type == INSN_JUMP_UNCONDITIONAL) {
172 struct instruction *dest = insn->jump_dest;
173 struct symbol *dest_func;
174
175 if (!dest)
176 /* sibling call to another file */
177 return false;
178
179 if (dest->sec != func->sec ||
180 dest->offset < func->offset ||
181 dest->offset >= func->offset + func->len) {
182 /* local sibling call */
183 dest_func = find_symbol_by_offset(dest->sec,
184 dest->offset);
185 if (!dest_func)
186 continue;
187
188 return dead_end_function(file, dest_func);
189 }
190 }
191
192 if (insn->type == INSN_JUMP_DYNAMIC)
193 /* sibling call */
194 return false;
195 }
196
197 return !empty;
198}
199
200/*
201 * Call the arch-specific instruction decoder for all the instructions and add
202 * them to the global insns list.
203 */
204static int decode_instructions(struct objtool_file *file)
205{
206 struct section *sec;
207 unsigned long offset;
208 struct instruction *insn;
209 int ret;
210
211 INIT_LIST_HEAD(&file->insns);
212
213 list_for_each_entry(sec, &file->elf->sections, list) {
214
215 if (!(sec->sh.sh_flags & SHF_EXECINSTR))
216 continue;
217
218 for (offset = 0; offset < sec->len; offset += insn->len) {
219 insn = malloc(sizeof(*insn));
220 memset(insn, 0, sizeof(*insn));
221
222 INIT_LIST_HEAD(&insn->alts);
223 insn->sec = sec;
224 insn->offset = offset;
225
226 ret = arch_decode_instruction(file->elf, sec, offset,
227 sec->len - offset,
228 &insn->len, &insn->type,
229 &insn->immediate);
230 if (ret)
231 return ret;
232
233 if (!insn->type || insn->type > INSN_LAST) {
234 WARN_FUNC("invalid instruction type %d",
235 insn->sec, insn->offset, insn->type);
236 return -1;
237 }
238
239 list_add_tail(&insn->list, &file->insns);
240 }
241 }
242
243 return 0;
244}
245
246/*
247 * Warnings shouldn't be reported for ignored functions.
248 */
249static void get_ignores(struct objtool_file *file)
250{
251 struct instruction *insn;
252 struct section *sec;
253 struct symbol *func;
254
255 list_for_each_entry(sec, &file->elf->sections, list) {
256 list_for_each_entry(func, &sec->symbols, list) {
257 if (func->type != STT_FUNC)
258 continue;
259
260 if (!ignore_func(file, func))
261 continue;
262
263 insn = find_instruction(file, sec, func->offset);
264 if (!insn)
265 continue;
266
267 list_for_each_entry_from(insn, &file->insns, list) {
268 if (insn->sec != func->sec ||
269 insn->offset >= func->offset + func->len)
270 break;
271
272 insn->visited = true;
273 }
274 }
275 }
276}
277
278/*
279 * Find the destination instructions for all jumps.
280 */
281static int get_jump_destinations(struct objtool_file *file)
282{
283 struct instruction *insn;
284 struct rela *rela;
285 struct section *dest_sec;
286 unsigned long dest_off;
287
288 list_for_each_entry(insn, &file->insns, list) {
289 if (insn->type != INSN_JUMP_CONDITIONAL &&
290 insn->type != INSN_JUMP_UNCONDITIONAL)
291 continue;
292
293 /* skip ignores */
294 if (insn->visited)
295 continue;
296
297 rela = find_rela_by_dest_range(insn->sec, insn->offset,
298 insn->len);
299 if (!rela) {
300 dest_sec = insn->sec;
301 dest_off = insn->offset + insn->len + insn->immediate;
302 } else if (rela->sym->type == STT_SECTION) {
303 dest_sec = rela->sym->sec;
304 dest_off = rela->addend + 4;
305 } else if (rela->sym->sec->idx) {
306 dest_sec = rela->sym->sec;
307 dest_off = rela->sym->sym.st_value + rela->addend + 4;
308 } else {
309 /* sibling call */
310 insn->jump_dest = 0;
311 continue;
312 }
313
314 insn->jump_dest = find_instruction(file, dest_sec, dest_off);
315 if (!insn->jump_dest) {
316
317 /*
318 * This is a special case where an alt instruction
319 * jumps past the end of the section. These are
320 * handled later in handle_group_alt().
321 */
322 if (!strcmp(insn->sec->name, ".altinstr_replacement"))
323 continue;
324
325 WARN_FUNC("can't find jump dest instruction at %s+0x%lx",
326 insn->sec, insn->offset, dest_sec->name,
327 dest_off);
328 return -1;
329 }
330 }
331
332 return 0;
333}
334
335/*
336 * Find the destination instructions for all calls.
337 */
338static int get_call_destinations(struct objtool_file *file)
339{
340 struct instruction *insn;
341 unsigned long dest_off;
342 struct rela *rela;
343
344 list_for_each_entry(insn, &file->insns, list) {
345 if (insn->type != INSN_CALL)
346 continue;
347
348 rela = find_rela_by_dest_range(insn->sec, insn->offset,
349 insn->len);
350 if (!rela) {
351 dest_off = insn->offset + insn->len + insn->immediate;
352 insn->call_dest = find_symbol_by_offset(insn->sec,
353 dest_off);
354 if (!insn->call_dest) {
355 WARN_FUNC("can't find call dest symbol at offset 0x%lx",
356 insn->sec, insn->offset, dest_off);
357 return -1;
358 }
359 } else if (rela->sym->type == STT_SECTION) {
360 insn->call_dest = find_symbol_by_offset(rela->sym->sec,
361 rela->addend+4);
362 if (!insn->call_dest ||
363 insn->call_dest->type != STT_FUNC) {
364 WARN_FUNC("can't find call dest symbol at %s+0x%x",
365 insn->sec, insn->offset,
366 rela->sym->sec->name,
367 rela->addend + 4);
368 return -1;
369 }
370 } else
371 insn->call_dest = rela->sym;
372 }
373
374 return 0;
375}
376
377/*
378 * The .alternatives section requires some extra special care, over and above
379 * what other special sections require:
380 *
381 * 1. Because alternatives are patched in-place, we need to insert a fake jump
382 * instruction at the end so that validate_branch() skips all the original
383 * replaced instructions when validating the new instruction path.
384 *
385 * 2. An added wrinkle is that the new instruction length might be zero. In
386 * that case the old instructions are replaced with noops. We simulate that
387 * by creating a fake jump as the only new instruction.
388 *
389 * 3. In some cases, the alternative section includes an instruction which
390 * conditionally jumps to the _end_ of the entry. We have to modify these
391 * jumps' destinations to point back to .text rather than the end of the
392 * entry in .altinstr_replacement.
393 *
394 * 4. It has been requested that we don't validate the !POPCNT feature path
395 * which is a "very very small percentage of machines".
396 */
397static int handle_group_alt(struct objtool_file *file,
398 struct special_alt *special_alt,
399 struct instruction *orig_insn,
400 struct instruction **new_insn)
401{
402 struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump;
403 unsigned long dest_off;
404
405 last_orig_insn = NULL;
406 insn = orig_insn;
407 list_for_each_entry_from(insn, &file->insns, list) {
408 if (insn->sec != special_alt->orig_sec ||
409 insn->offset >= special_alt->orig_off + special_alt->orig_len)
410 break;
411
412 if (special_alt->skip_orig)
413 insn->type = INSN_NOP;
414
415 insn->alt_group = true;
416 last_orig_insn = insn;
417 }
418
419 if (list_is_last(&last_orig_insn->list, &file->insns) ||
420 list_next_entry(last_orig_insn, list)->sec != special_alt->orig_sec) {
421 WARN("%s: don't know how to handle alternatives at end of section",
422 special_alt->orig_sec->name);
423 return -1;
424 }
425
426 fake_jump = malloc(sizeof(*fake_jump));
427 if (!fake_jump) {
428 WARN("malloc failed");
429 return -1;
430 }
431 memset(fake_jump, 0, sizeof(*fake_jump));
432 INIT_LIST_HEAD(&fake_jump->alts);
433 fake_jump->sec = special_alt->new_sec;
434 fake_jump->offset = -1;
435 fake_jump->type = INSN_JUMP_UNCONDITIONAL;
436 fake_jump->jump_dest = list_next_entry(last_orig_insn, list);
437
438 if (!special_alt->new_len) {
439 *new_insn = fake_jump;
440 return 0;
441 }
442
443 last_new_insn = NULL;
444 insn = *new_insn;
445 list_for_each_entry_from(insn, &file->insns, list) {
446 if (insn->sec != special_alt->new_sec ||
447 insn->offset >= special_alt->new_off + special_alt->new_len)
448 break;
449
450 last_new_insn = insn;
451
452 if (insn->type != INSN_JUMP_CONDITIONAL &&
453 insn->type != INSN_JUMP_UNCONDITIONAL)
454 continue;
455
456 if (!insn->immediate)
457 continue;
458
459 dest_off = insn->offset + insn->len + insn->immediate;
460 if (dest_off == special_alt->new_off + special_alt->new_len)
461 insn->jump_dest = fake_jump;
462
463 if (!insn->jump_dest) {
464 WARN_FUNC("can't find alternative jump destination",
465 insn->sec, insn->offset);
466 return -1;
467 }
468 }
469
470 if (!last_new_insn) {
471 WARN_FUNC("can't find last new alternative instruction",
472 special_alt->new_sec, special_alt->new_off);
473 return -1;
474 }
475
476 list_add(&fake_jump->list, &last_new_insn->list);
477
478 return 0;
479}
480
481/*
482 * A jump table entry can either convert a nop to a jump or a jump to a nop.
483 * If the original instruction is a jump, make the alt entry an effective nop
484 * by just skipping the original instruction.
485 */
486static int handle_jump_alt(struct objtool_file *file,
487 struct special_alt *special_alt,
488 struct instruction *orig_insn,
489 struct instruction **new_insn)
490{
491 if (orig_insn->type == INSN_NOP)
492 return 0;
493
494 if (orig_insn->type != INSN_JUMP_UNCONDITIONAL) {
495 WARN_FUNC("unsupported instruction at jump label",
496 orig_insn->sec, orig_insn->offset);
497 return -1;
498 }
499
500 *new_insn = list_next_entry(orig_insn, list);
501 return 0;
502}
503
504/*
505 * Read all the special sections which have alternate instructions which can be
506 * patched in or redirected to at runtime. Each instruction having alternate
507 * instruction(s) has them added to its insn->alts list, which will be
508 * traversed in validate_branch().
509 */
510static int get_special_section_alts(struct objtool_file *file)
511{
512 struct list_head special_alts;
513 struct instruction *orig_insn, *new_insn;
514 struct special_alt *special_alt, *tmp;
515 struct alternative *alt;
516 int ret;
517
518 ret = special_get_alts(file->elf, &special_alts);
519 if (ret)
520 return ret;
521
522 list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {
523 alt = malloc(sizeof(*alt));
524 if (!alt) {
525 WARN("malloc failed");
526 ret = -1;
527 goto out;
528 }
529
530 orig_insn = find_instruction(file, special_alt->orig_sec,
531 special_alt->orig_off);
532 if (!orig_insn) {
533 WARN_FUNC("special: can't find orig instruction",
534 special_alt->orig_sec, special_alt->orig_off);
535 ret = -1;
536 goto out;
537 }
538
539 new_insn = NULL;
540 if (!special_alt->group || special_alt->new_len) {
541 new_insn = find_instruction(file, special_alt->new_sec,
542 special_alt->new_off);
543 if (!new_insn) {
544 WARN_FUNC("special: can't find new instruction",
545 special_alt->new_sec,
546 special_alt->new_off);
547 ret = -1;
548 goto out;
549 }
550 }
551
552 if (special_alt->group) {
553 ret = handle_group_alt(file, special_alt, orig_insn,
554 &new_insn);
555 if (ret)
556 goto out;
557 } else if (special_alt->jump_or_nop) {
558 ret = handle_jump_alt(file, special_alt, orig_insn,
559 &new_insn);
560 if (ret)
561 goto out;
562 }
563
564 alt->insn = new_insn;
565 list_add_tail(&alt->list, &orig_insn->alts);
566
567 list_del(&special_alt->list);
568 free(special_alt);
569 }
570
571out:
572 return ret;
573}
574
575/*
576 * For some switch statements, gcc generates a jump table in the .rodata
577 * section which contains a list of addresses within the function to jump to.
578 * This finds these jump tables and adds them to the insn->alts lists.
579 */
580static int get_switch_alts(struct objtool_file *file)
581{
582 struct instruction *insn, *alt_insn;
583 struct rela *rodata_rela, *rela;
584 struct section *rodata;
585 struct symbol *func;
586 struct alternative *alt;
587
588 list_for_each_entry(insn, &file->insns, list) {
589 if (insn->type != INSN_JUMP_DYNAMIC)
590 continue;
591
592 rodata_rela = find_rela_by_dest_range(insn->sec, insn->offset,
593 insn->len);
594 if (!rodata_rela || strcmp(rodata_rela->sym->name, ".rodata"))
595 continue;
596
597 rodata = find_section_by_name(file->elf, ".rodata");
598 if (!rodata || !rodata->rela)
599 continue;
600
601 /* common case: jmpq *[addr](,%rax,8) */
602 rela = find_rela_by_dest(rodata, rodata_rela->addend);
603
604 /* rare case: jmpq *[addr](%rip) */
605 if (!rela)
606 rela = find_rela_by_dest(rodata,
607 rodata_rela->addend + 4);
608 if (!rela)
609 continue;
610
611 func = find_containing_func(insn->sec, insn->offset);
612 if (!func) {
613 WARN_FUNC("can't find containing func",
614 insn->sec, insn->offset);
615 return -1;
616 }
617
618 list_for_each_entry_from(rela, &rodata->rela->relas, list) {
619 if (rela->sym->sec != insn->sec ||
620 rela->addend <= func->offset ||
621 rela->addend >= func->offset + func->len)
622 break;
623
624 alt_insn = find_instruction(file, insn->sec,
625 rela->addend);
626 if (!alt_insn) {
627 WARN("%s: can't find instruction at %s+0x%x",
628 rodata->rela->name, insn->sec->name,
629 rela->addend);
630 return -1;
631 }
632
633 alt = malloc(sizeof(*alt));
634 if (!alt) {
635 WARN("malloc failed");
636 return -1;
637 }
638
639 alt->insn = alt_insn;
640 list_add_tail(&alt->list, &insn->alts);
641 }
642 }
643
644 return 0;
645}
646
647static int decode_sections(struct objtool_file *file)
648{
649 int ret;
650
651 ret = decode_instructions(file);
652 if (ret)
653 return ret;
654
655 get_ignores(file);
656
657 ret = get_jump_destinations(file);
658 if (ret)
659 return ret;
660
661 ret = get_call_destinations(file);
662 if (ret)
663 return ret;
664
665 ret = get_special_section_alts(file);
666 if (ret)
667 return ret;
668
669 ret = get_switch_alts(file);
670 if (ret)
671 return ret;
672
673 return 0;
674}
675
676static bool is_fentry_call(struct instruction *insn)
677{
678 if (insn->type == INSN_CALL &&
679 insn->call_dest->type == STT_NOTYPE &&
680 !strcmp(insn->call_dest->name, "__fentry__"))
681 return true;
682
683 return false;
684}
685
686static bool has_modified_stack_frame(struct instruction *insn)
687{
688 return (insn->state & STATE_FP_SAVED) ||
689 (insn->state & STATE_FP_SETUP);
690}
691
692static bool has_valid_stack_frame(struct instruction *insn)
693{
694 return (insn->state & STATE_FP_SAVED) &&
695 (insn->state & STATE_FP_SETUP);
696}
697
698/*
699 * Follow the branch starting at the given instruction, and recursively follow
700 * any other branches (jumps). Meanwhile, track the frame pointer state at
701 * each instruction and validate all the rules described in
702 * tools/objtool/Documentation/stack-validation.txt.
703 */
704static int validate_branch(struct objtool_file *file,
705 struct instruction *first, unsigned char first_state)
706{
707 struct alternative *alt;
708 struct instruction *insn;
709 struct section *sec;
710 unsigned char state;
711 int ret, warnings = 0;
712
713 insn = first;
714 sec = insn->sec;
715 state = first_state;
716
717 if (insn->alt_group && list_empty(&insn->alts)) {
718 WARN_FUNC("don't know how to handle branch to middle of alternative instruction group",
719 sec, insn->offset);
720 warnings++;
721 }
722
723 while (1) {
724 if (insn->visited) {
725 if (insn->state != state) {
726 WARN_FUNC("frame pointer state mismatch",
727 sec, insn->offset);
728 warnings++;
729 }
730
731 return warnings;
732 }
733
734 /*
735 * Catch a rare case where a noreturn function falls through to
736 * the next function.
737 */
738 if (is_fentry_call(insn) && (state & STATE_FENTRY))
739 return warnings;
740
741 insn->visited = true;
742 insn->state = state;
743
744 list_for_each_entry(alt, &insn->alts, list) {
745 ret = validate_branch(file, alt->insn, state);
746 warnings += ret;
747 }
748
749 switch (insn->type) {
750
751 case INSN_FP_SAVE:
752 if (!nofp) {
753 if (state & STATE_FP_SAVED) {
754 WARN_FUNC("duplicate frame pointer save",
755 sec, insn->offset);
756 warnings++;
757 }
758 state |= STATE_FP_SAVED;
759 }
760 break;
761
762 case INSN_FP_SETUP:
763 if (!nofp) {
764 if (state & STATE_FP_SETUP) {
765 WARN_FUNC("duplicate frame pointer setup",
766 sec, insn->offset);
767 warnings++;
768 }
769 state |= STATE_FP_SETUP;
770 }
771 break;
772
773 case INSN_FP_RESTORE:
774 if (!nofp) {
775 if (has_valid_stack_frame(insn))
776 state &= ~STATE_FP_SETUP;
777
778 state &= ~STATE_FP_SAVED;
779 }
780 break;
781
782 case INSN_RETURN:
783 if (!nofp && has_modified_stack_frame(insn)) {
784 WARN_FUNC("return without frame pointer restore",
785 sec, insn->offset);
786 warnings++;
787 }
788 return warnings;
789
790 case INSN_CALL:
791 if (is_fentry_call(insn)) {
792 state |= STATE_FENTRY;
793 break;
794 }
795
796 if (dead_end_function(file, insn->call_dest))
797 return warnings;
798
799 /* fallthrough */
800 case INSN_CALL_DYNAMIC:
801 if (!nofp && !has_valid_stack_frame(insn)) {
802 WARN_FUNC("call without frame pointer save/setup",
803 sec, insn->offset);
804 warnings++;
805 }
806 break;
807
808 case INSN_JUMP_CONDITIONAL:
809 case INSN_JUMP_UNCONDITIONAL:
810 if (insn->jump_dest) {
811 ret = validate_branch(file, insn->jump_dest,
812 state);
813 warnings += ret;
814 } else if (has_modified_stack_frame(insn)) {
815 WARN_FUNC("sibling call from callable instruction with changed frame pointer",
816 sec, insn->offset);
817 warnings++;
818 } /* else it's a sibling call */
819
820 if (insn->type == INSN_JUMP_UNCONDITIONAL)
821 return warnings;
822
823 break;
824
825 case INSN_JUMP_DYNAMIC:
826 if (list_empty(&insn->alts) &&
827 has_modified_stack_frame(insn)) {
828 WARN_FUNC("sibling call from callable instruction with changed frame pointer",
829 sec, insn->offset);
830 warnings++;
831 }
832
833 return warnings;
834
835 case INSN_BUG:
836 return warnings;
837
838 default:
839 break;
840 }
841
842 insn = list_next_entry(insn, list);
843
844 if (&insn->list == &file->insns || insn->sec != sec) {
845 WARN("%s: unexpected end of section", sec->name);
846 warnings++;
847 return warnings;
848 }
849 }
850
851 return warnings;
852}
853
854static bool is_gcov_insn(struct instruction *insn)
855{
856 struct rela *rela;
857 struct section *sec;
858 struct symbol *sym;
859 unsigned long offset;
860
861 rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len);
862 if (!rela)
863 return false;
864
865 if (rela->sym->type != STT_SECTION)
866 return false;
867
868 sec = rela->sym->sec;
869 offset = rela->addend + insn->offset + insn->len - rela->offset;
870
871 list_for_each_entry(sym, &sec->symbols, list) {
872 if (sym->type != STT_OBJECT)
873 continue;
874
875 if (offset >= sym->offset && offset < sym->offset + sym->len)
876 return (!memcmp(sym->name, "__gcov0.", 8));
877 }
878
879 return false;
880}
881
882static bool is_kasan_insn(struct instruction *insn)
883{
884 return (insn->type == INSN_CALL &&
885 !strcmp(insn->call_dest->name, "__asan_handle_no_return"));
886}
887
888static bool is_ubsan_insn(struct instruction *insn)
889{
890 return (insn->type == INSN_CALL &&
891 !strcmp(insn->call_dest->name,
892 "__ubsan_handle_builtin_unreachable"));
893}
894
895static bool ignore_unreachable_insn(struct instruction *insn,
896 unsigned long func_end)
897{
898 int i;
899
900 if (insn->type == INSN_NOP)
901 return true;
902
903 if (is_gcov_insn(insn))
904 return true;
905
906 /*
907 * Check if this (or a subsequent) instruction is related to
908 * CONFIG_UBSAN or CONFIG_KASAN.
909 *
910 * End the search at 5 instructions to avoid going into the weeds.
911 */
912 for (i = 0; i < 5; i++) {
913
914 if (is_kasan_insn(insn) || is_ubsan_insn(insn))
915 return true;
916
917 if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest) {
918 insn = insn->jump_dest;
919 continue;
920 }
921
922 if (insn->offset + insn->len >= func_end)
923 break;
924 insn = list_next_entry(insn, list);
925 }
926
927 return false;
928}
929
930static int validate_functions(struct objtool_file *file)
931{
932 struct section *sec;
933 struct symbol *func;
934 struct instruction *insn;
935 unsigned long func_end;
936 int ret, warnings = 0;
937
938 list_for_each_entry(sec, &file->elf->sections, list) {
939 list_for_each_entry(func, &sec->symbols, list) {
940 if (func->type != STT_FUNC)
941 continue;
942
943 insn = find_instruction(file, sec, func->offset);
944 if (!insn) {
945 WARN("%s(): can't find starting instruction",
946 func->name);
947 warnings++;
948 continue;
949 }
950
951 ret = validate_branch(file, insn, 0);
952 warnings += ret;
953 }
954 }
955
956 list_for_each_entry(sec, &file->elf->sections, list) {
957 list_for_each_entry(func, &sec->symbols, list) {
958 if (func->type != STT_FUNC)
959 continue;
960
961 insn = find_instruction(file, sec, func->offset);
962 if (!insn)
963 continue;
964
965 func_end = func->offset + func->len;
966
967 list_for_each_entry_from(insn, &file->insns, list) {
968 if (insn->sec != func->sec ||
969 insn->offset >= func_end)
970 break;
971
972 if (insn->visited)
973 continue;
974
975 if (!ignore_unreachable_insn(insn, func_end)) {
976 WARN_FUNC("function has unreachable instruction", insn->sec, insn->offset);
977 warnings++;
978 }
979
980 insn->visited = true;
981 }
982 }
983 }
984
985 return warnings;
986}
987
988static int validate_uncallable_instructions(struct objtool_file *file)
989{
990 struct instruction *insn;
991 int warnings = 0;
992
993 list_for_each_entry(insn, &file->insns, list) {
994 if (!insn->visited && insn->type == INSN_RETURN) {
995 WARN_FUNC("return instruction outside of a callable function",
996 insn->sec, insn->offset);
997 warnings++;
998 }
999 }
1000
1001 return warnings;
1002}
1003
1004static void cleanup(struct objtool_file *file)
1005{
1006 struct instruction *insn, *tmpinsn;
1007 struct alternative *alt, *tmpalt;
1008
1009 list_for_each_entry_safe(insn, tmpinsn, &file->insns, list) {
1010 list_for_each_entry_safe(alt, tmpalt, &insn->alts, list) {
1011 list_del(&alt->list);
1012 free(alt);
1013 }
1014 list_del(&insn->list);
1015 free(insn);
1016 }
1017 elf_close(file->elf);
1018}
1019
1020const char * const check_usage[] = {
1021 "objtool check [<options>] file.o",
1022 NULL,
1023};
1024
1025int cmd_check(int argc, const char **argv)
1026{
1027 struct objtool_file file;
1028 int ret, warnings = 0;
1029
1030 const struct option options[] = {
1031 OPT_BOOLEAN('f', "no-fp", &nofp, "Skip frame pointer validation"),
1032 OPT_END(),
1033 };
1034
1035 argc = parse_options(argc, argv, options, check_usage, 0);
1036
1037 if (argc != 1)
1038 usage_with_options(check_usage, options);
1039
1040 objname = argv[0];
1041
1042 file.elf = elf_open(objname);
1043 if (!file.elf) {
1044 fprintf(stderr, "error reading elf file %s\n", objname);
1045 return 1;
1046 }
1047
1048 INIT_LIST_HEAD(&file.insns);
1049
1050 ret = decode_sections(&file);
1051 if (ret < 0)
1052 goto out;
1053 warnings += ret;
1054
1055 ret = validate_functions(&file);
1056 if (ret < 0)
1057 goto out;
1058 warnings += ret;
1059
1060 ret = validate_uncallable_instructions(&file);
1061 if (ret < 0)
1062 goto out;
1063 warnings += ret;
1064
1065out:
1066 cleanup(&file);
1067
1068 /* ignore warnings for now until we get all the code cleaned up */
1069 if (ret || warnings)
1070 return 0;
1071 return 0;
1072}