2 * Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
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.
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.
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/>.
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.
25 * For more information, see tools/objtool/Documentation/stack-validation.txt.
29 #include <subcmd/parse-options.h>
37 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
39 #define STATE_FP_SAVED 0x1
40 #define STATE_FP_SETUP 0x2
41 #define STATE_FENTRY 0x4
44 struct list_head list
;
47 unsigned int len
, state
;
49 unsigned long immediate
;
50 bool alt_group
, visited
;
51 struct symbol
*call_dest
;
52 struct instruction
*jump_dest
;
53 struct list_head alts
;
57 struct list_head list
;
58 struct instruction
*insn
;
63 struct list_head insns
;
69 static struct instruction
*find_instruction(struct objtool_file
*file
,
73 struct instruction
*insn
;
75 list_for_each_entry(insn
, &file
->insns
, list
)
76 if (insn
->sec
== sec
&& insn
->offset
== offset
)
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.
87 static bool ignore_func(struct objtool_file
*file
, struct symbol
*func
)
89 struct section
*macro_sec
;
91 struct instruction
*insn
;
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
, ¯o_sec
->rela
->relas
, list
)
97 if (rela
->sym
->sec
== func
->sec
&&
98 rela
->addend
== func
->offset
)
101 /* check if it has a context switching instruction */
102 insn
= find_instruction(file
, func
->sec
, func
->offset
);
105 list_for_each_entry_from(insn
, &file
->insns
, list
) {
106 if (insn
->sec
!= func
->sec
||
107 insn
->offset
>= func
->offset
+ func
->len
)
109 if (insn
->type
== INSN_CONTEXT_SWITCH
)
117 * This checks to see if the given function is a "noreturn" function.
119 * For global functions which are outside the scope of this object file, we
120 * have to keep a manual list of them.
122 * For local functions, we have to detect them manually by simply looking for
123 * the lack of a return instruction.
125 static bool dead_end_function(struct objtool_file
*file
, struct symbol
*func
)
128 struct instruction
*insn
;
132 * Unfortunately these have to be hard coded because the noreturn
133 * attribute isn't provided in ELF data.
135 static const char * const global_noreturns
[] = {
139 "__module_put_and_exit",
141 "kvm_spurious_fault",
146 if (func
->bind
== STB_WEAK
)
149 if (func
->bind
== STB_GLOBAL
)
150 for (i
= 0; i
< ARRAY_SIZE(global_noreturns
); i
++)
151 if (!strcmp(func
->name
, global_noreturns
[i
]))
157 insn
= find_instruction(file
, func
->sec
, func
->offset
);
161 list_for_each_entry_from(insn
, &file
->insns
, list
) {
162 if (insn
->sec
!= func
->sec
||
163 insn
->offset
>= func
->offset
+ func
->len
)
168 if (insn
->type
== INSN_RETURN
)
171 if (insn
->type
== INSN_JUMP_UNCONDITIONAL
) {
172 struct instruction
*dest
= insn
->jump_dest
;
173 struct symbol
*dest_func
;
176 /* sibling call to another file */
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
,
188 return dead_end_function(file
, dest_func
);
192 if (insn
->type
== INSN_JUMP_DYNAMIC
)
201 * Call the arch-specific instruction decoder for all the instructions and add
202 * them to the global insns list.
204 static int decode_instructions(struct objtool_file
*file
)
207 unsigned long offset
;
208 struct instruction
*insn
;
211 INIT_LIST_HEAD(&file
->insns
);
213 list_for_each_entry(sec
, &file
->elf
->sections
, list
) {
215 if (!(sec
->sh
.sh_flags
& SHF_EXECINSTR
))
218 for (offset
= 0; offset
< sec
->len
; offset
+= insn
->len
) {
219 insn
= malloc(sizeof(*insn
));
220 memset(insn
, 0, sizeof(*insn
));
222 INIT_LIST_HEAD(&insn
->alts
);
224 insn
->offset
= offset
;
226 ret
= arch_decode_instruction(file
->elf
, sec
, offset
,
228 &insn
->len
, &insn
->type
,
233 if (!insn
->type
|| insn
->type
> INSN_LAST
) {
234 WARN_FUNC("invalid instruction type %d",
235 insn
->sec
, insn
->offset
, insn
->type
);
239 list_add_tail(&insn
->list
, &file
->insns
);
247 * Warnings shouldn't be reported for ignored functions.
249 static void get_ignores(struct objtool_file
*file
)
251 struct instruction
*insn
;
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
)
260 if (!ignore_func(file
, func
))
263 insn
= find_instruction(file
, sec
, func
->offset
);
267 list_for_each_entry_from(insn
, &file
->insns
, list
) {
268 if (insn
->sec
!= func
->sec
||
269 insn
->offset
>= func
->offset
+ func
->len
)
272 insn
->visited
= true;
279 * Find the destination instructions for all jumps.
281 static int get_jump_destinations(struct objtool_file
*file
)
283 struct instruction
*insn
;
285 struct section
*dest_sec
;
286 unsigned long dest_off
;
288 list_for_each_entry(insn
, &file
->insns
, list
) {
289 if (insn
->type
!= INSN_JUMP_CONDITIONAL
&&
290 insn
->type
!= INSN_JUMP_UNCONDITIONAL
)
297 rela
= find_rela_by_dest_range(insn
->sec
, insn
->offset
,
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;
314 insn
->jump_dest
= find_instruction(file
, dest_sec
, dest_off
);
315 if (!insn
->jump_dest
) {
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().
322 if (!strcmp(insn
->sec
->name
, ".altinstr_replacement"))
325 WARN_FUNC("can't find jump dest instruction at %s+0x%lx",
326 insn
->sec
, insn
->offset
, dest_sec
->name
,
336 * Find the destination instructions for all calls.
338 static int get_call_destinations(struct objtool_file
*file
)
340 struct instruction
*insn
;
341 unsigned long dest_off
;
344 list_for_each_entry(insn
, &file
->insns
, list
) {
345 if (insn
->type
!= INSN_CALL
)
348 rela
= find_rela_by_dest_range(insn
->sec
, insn
->offset
,
351 dest_off
= insn
->offset
+ insn
->len
+ insn
->immediate
;
352 insn
->call_dest
= find_symbol_by_offset(insn
->sec
,
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
);
359 } else if (rela
->sym
->type
== STT_SECTION
) {
360 insn
->call_dest
= find_symbol_by_offset(rela
->sym
->sec
,
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
,
371 insn
->call_dest
= rela
->sym
;
378 * The .alternatives section requires some extra special care, over and above
379 * what other special sections require:
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.
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.
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.
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".
397 static int handle_group_alt(struct objtool_file
*file
,
398 struct special_alt
*special_alt
,
399 struct instruction
*orig_insn
,
400 struct instruction
**new_insn
)
402 struct instruction
*last_orig_insn
, *last_new_insn
, *insn
, *fake_jump
;
403 unsigned long dest_off
;
405 last_orig_insn
= NULL
;
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
)
412 if (special_alt
->skip_orig
)
413 insn
->type
= INSN_NOP
;
415 insn
->alt_group
= true;
416 last_orig_insn
= insn
;
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
);
426 fake_jump
= malloc(sizeof(*fake_jump
));
428 WARN("malloc failed");
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
);
438 if (!special_alt
->new_len
) {
439 *new_insn
= fake_jump
;
443 last_new_insn
= NULL
;
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
)
450 last_new_insn
= insn
;
452 if (insn
->type
!= INSN_JUMP_CONDITIONAL
&&
453 insn
->type
!= INSN_JUMP_UNCONDITIONAL
)
456 if (!insn
->immediate
)
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
;
463 if (!insn
->jump_dest
) {
464 WARN_FUNC("can't find alternative jump destination",
465 insn
->sec
, insn
->offset
);
470 if (!last_new_insn
) {
471 WARN_FUNC("can't find last new alternative instruction",
472 special_alt
->new_sec
, special_alt
->new_off
);
476 list_add(&fake_jump
->list
, &last_new_insn
->list
);
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.
486 static int handle_jump_alt(struct objtool_file
*file
,
487 struct special_alt
*special_alt
,
488 struct instruction
*orig_insn
,
489 struct instruction
**new_insn
)
491 if (orig_insn
->type
== INSN_NOP
)
494 if (orig_insn
->type
!= INSN_JUMP_UNCONDITIONAL
) {
495 WARN_FUNC("unsupported instruction at jump label",
496 orig_insn
->sec
, orig_insn
->offset
);
500 *new_insn
= list_next_entry(orig_insn
, list
);
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().
510 static int get_special_section_alts(struct objtool_file
*file
)
512 struct list_head special_alts
;
513 struct instruction
*orig_insn
, *new_insn
;
514 struct special_alt
*special_alt
, *tmp
;
515 struct alternative
*alt
;
518 ret
= special_get_alts(file
->elf
, &special_alts
);
522 list_for_each_entry_safe(special_alt
, tmp
, &special_alts
, list
) {
523 alt
= malloc(sizeof(*alt
));
525 WARN("malloc failed");
530 orig_insn
= find_instruction(file
, special_alt
->orig_sec
,
531 special_alt
->orig_off
);
533 WARN_FUNC("special: can't find orig instruction",
534 special_alt
->orig_sec
, special_alt
->orig_off
);
540 if (!special_alt
->group
|| special_alt
->new_len
) {
541 new_insn
= find_instruction(file
, special_alt
->new_sec
,
542 special_alt
->new_off
);
544 WARN_FUNC("special: can't find new instruction",
545 special_alt
->new_sec
,
546 special_alt
->new_off
);
552 if (special_alt
->group
) {
553 ret
= handle_group_alt(file
, special_alt
, orig_insn
,
557 } else if (special_alt
->jump_or_nop
) {
558 ret
= handle_jump_alt(file
, special_alt
, orig_insn
,
564 alt
->insn
= new_insn
;
565 list_add_tail(&alt
->list
, &orig_insn
->alts
);
567 list_del(&special_alt
->list
);
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.
580 static int get_switch_alts(struct objtool_file
*file
)
582 struct instruction
*insn
, *alt_insn
;
583 struct rela
*rodata_rela
, *rela
;
584 struct section
*rodata
;
586 struct alternative
*alt
;
588 list_for_each_entry(insn
, &file
->insns
, list
) {
589 if (insn
->type
!= INSN_JUMP_DYNAMIC
)
592 rodata_rela
= find_rela_by_dest_range(insn
->sec
, insn
->offset
,
594 if (!rodata_rela
|| strcmp(rodata_rela
->sym
->name
, ".rodata"))
597 rodata
= find_section_by_name(file
->elf
, ".rodata");
598 if (!rodata
|| !rodata
->rela
)
601 /* common case: jmpq *[addr](,%rax,8) */
602 rela
= find_rela_by_dest(rodata
, rodata_rela
->addend
);
604 /* rare case: jmpq *[addr](%rip) */
606 rela
= find_rela_by_dest(rodata
,
607 rodata_rela
->addend
+ 4);
611 func
= find_containing_func(insn
->sec
, insn
->offset
);
613 WARN_FUNC("can't find containing func",
614 insn
->sec
, insn
->offset
);
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
)
624 alt_insn
= find_instruction(file
, insn
->sec
,
627 WARN("%s: can't find instruction at %s+0x%x",
628 rodata
->rela
->name
, insn
->sec
->name
,
633 alt
= malloc(sizeof(*alt
));
635 WARN("malloc failed");
639 alt
->insn
= alt_insn
;
640 list_add_tail(&alt
->list
, &insn
->alts
);
647 static int decode_sections(struct objtool_file
*file
)
651 ret
= decode_instructions(file
);
657 ret
= get_jump_destinations(file
);
661 ret
= get_call_destinations(file
);
665 ret
= get_special_section_alts(file
);
669 ret
= get_switch_alts(file
);
676 static bool is_fentry_call(struct instruction
*insn
)
678 if (insn
->type
== INSN_CALL
&&
679 insn
->call_dest
->type
== STT_NOTYPE
&&
680 !strcmp(insn
->call_dest
->name
, "__fentry__"))
686 static bool has_modified_stack_frame(struct instruction
*insn
)
688 return (insn
->state
& STATE_FP_SAVED
) ||
689 (insn
->state
& STATE_FP_SETUP
);
692 static bool has_valid_stack_frame(struct instruction
*insn
)
694 return (insn
->state
& STATE_FP_SAVED
) &&
695 (insn
->state
& STATE_FP_SETUP
);
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.
704 static int validate_branch(struct objtool_file
*file
,
705 struct instruction
*first
, unsigned char first_state
)
707 struct alternative
*alt
;
708 struct instruction
*insn
;
711 int ret
, warnings
= 0;
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",
725 if (insn
->state
!= state
) {
726 WARN_FUNC("frame pointer state mismatch",
735 * Catch a rare case where a noreturn function falls through to
738 if (is_fentry_call(insn
) && (state
& STATE_FENTRY
))
741 insn
->visited
= true;
744 list_for_each_entry(alt
, &insn
->alts
, list
) {
745 ret
= validate_branch(file
, alt
->insn
, state
);
749 switch (insn
->type
) {
753 if (state
& STATE_FP_SAVED
) {
754 WARN_FUNC("duplicate frame pointer save",
758 state
|= STATE_FP_SAVED
;
764 if (state
& STATE_FP_SETUP
) {
765 WARN_FUNC("duplicate frame pointer setup",
769 state
|= STATE_FP_SETUP
;
773 case INSN_FP_RESTORE
:
775 if (has_valid_stack_frame(insn
))
776 state
&= ~STATE_FP_SETUP
;
778 state
&= ~STATE_FP_SAVED
;
783 if (!nofp
&& has_modified_stack_frame(insn
)) {
784 WARN_FUNC("return without frame pointer restore",
791 if (is_fentry_call(insn
)) {
792 state
|= STATE_FENTRY
;
796 if (dead_end_function(file
, insn
->call_dest
))
800 case INSN_CALL_DYNAMIC
:
801 if (!nofp
&& !has_valid_stack_frame(insn
)) {
802 WARN_FUNC("call without frame pointer save/setup",
808 case INSN_JUMP_CONDITIONAL
:
809 case INSN_JUMP_UNCONDITIONAL
:
810 if (insn
->jump_dest
) {
811 ret
= validate_branch(file
, insn
->jump_dest
,
814 } else if (has_modified_stack_frame(insn
)) {
815 WARN_FUNC("sibling call from callable instruction with changed frame pointer",
818 } /* else it's a sibling call */
820 if (insn
->type
== INSN_JUMP_UNCONDITIONAL
)
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",
842 insn
= list_next_entry(insn
, list
);
844 if (&insn
->list
== &file
->insns
|| insn
->sec
!= sec
) {
845 WARN("%s: unexpected end of section", sec
->name
);
854 static bool is_gcov_insn(struct instruction
*insn
)
859 unsigned long offset
;
861 rela
= find_rela_by_dest_range(insn
->sec
, insn
->offset
, insn
->len
);
865 if (rela
->sym
->type
!= STT_SECTION
)
868 sec
= rela
->sym
->sec
;
869 offset
= rela
->addend
+ insn
->offset
+ insn
->len
- rela
->offset
;
871 list_for_each_entry(sym
, &sec
->symbols
, list
) {
872 if (sym
->type
!= STT_OBJECT
)
875 if (offset
>= sym
->offset
&& offset
< sym
->offset
+ sym
->len
)
876 return (!memcmp(sym
->name
, "__gcov0.", 8));
882 static bool is_kasan_insn(struct instruction
*insn
)
884 return (insn
->type
== INSN_CALL
&&
885 !strcmp(insn
->call_dest
->name
, "__asan_handle_no_return"));
888 static bool is_ubsan_insn(struct instruction
*insn
)
890 return (insn
->type
== INSN_CALL
&&
891 !strcmp(insn
->call_dest
->name
,
892 "__ubsan_handle_builtin_unreachable"));
895 static bool ignore_unreachable_insn(struct instruction
*insn
,
896 unsigned long func_end
)
900 if (insn
->type
== INSN_NOP
)
903 if (is_gcov_insn(insn
))
907 * Check if this (or a subsequent) instruction is related to
908 * CONFIG_UBSAN or CONFIG_KASAN.
910 * End the search at 5 instructions to avoid going into the weeds.
912 for (i
= 0; i
< 5; i
++) {
914 if (is_kasan_insn(insn
) || is_ubsan_insn(insn
))
917 if (insn
->type
== INSN_JUMP_UNCONDITIONAL
&& insn
->jump_dest
) {
918 insn
= insn
->jump_dest
;
922 if (insn
->offset
+ insn
->len
>= func_end
)
924 insn
= list_next_entry(insn
, list
);
930 static int validate_functions(struct objtool_file
*file
)
934 struct instruction
*insn
;
935 unsigned long func_end
;
936 int ret
, warnings
= 0;
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
)
943 insn
= find_instruction(file
, sec
, func
->offset
);
945 WARN("%s(): can't find starting instruction",
951 ret
= validate_branch(file
, insn
, 0);
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
)
961 insn
= find_instruction(file
, sec
, func
->offset
);
965 func_end
= func
->offset
+ func
->len
;
967 list_for_each_entry_from(insn
, &file
->insns
, list
) {
968 if (insn
->sec
!= func
->sec
||
969 insn
->offset
>= func_end
)
975 if (!ignore_unreachable_insn(insn
, func_end
)) {
976 WARN_FUNC("function has unreachable instruction", insn
->sec
, insn
->offset
);
980 insn
->visited
= true;
988 static int validate_uncallable_instructions(struct objtool_file
*file
)
990 struct instruction
*insn
;
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
);
1004 static void cleanup(struct objtool_file
*file
)
1006 struct instruction
*insn
, *tmpinsn
;
1007 struct alternative
*alt
, *tmpalt
;
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
);
1014 list_del(&insn
->list
);
1017 elf_close(file
->elf
);
1020 const char * const check_usage
[] = {
1021 "objtool check [<options>] file.o",
1025 int cmd_check(int argc
, const char **argv
)
1027 struct objtool_file file
;
1028 int ret
, warnings
= 0;
1030 const struct option options
[] = {
1031 OPT_BOOLEAN('f', "no-fp", &nofp
, "Skip frame pointer validation"),
1035 argc
= parse_options(argc
, argv
, options
, check_usage
, 0);
1038 usage_with_options(check_usage
, options
);
1042 file
.elf
= elf_open(objname
);
1044 fprintf(stderr
, "error reading elf file %s\n", objname
);
1048 INIT_LIST_HEAD(&file
.insns
);
1050 ret
= decode_sections(&file
);
1055 ret
= validate_functions(&file
);
1060 ret
= validate_uncallable_instructions(&file
);
1068 /* ignore warnings for now until we get all the code cleaned up */
1069 if (ret
|| warnings
)
This page took 0.056162 seconds and 5 git commands to generate.