objtool: Add helper macros for traversing instructions
authorJosh Poimboeuf <jpoimboe@redhat.com>
Wed, 9 Mar 2016 06:06:55 +0000 (00:06 -0600)
committerIngo Molnar <mingo@kernel.org>
Wed, 9 Mar 2016 09:48:08 +0000 (10:48 +0100)
Add some helper macros to make it easier to traverse instructions, and
to abstract the details of the instruction list implementation in
preparation for creating a hash structure.

Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Bernd Petrovitsch <bernd@petrovitsch.priv.at>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Chris J Arges <chris.j.arges@canonical.com>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Michal Marek <mmarek@suse.cz>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Pedro Alves <palves@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: live-patching@vger.kernel.org
Link: http://lkml.kernel.org/r/8e1715d5035bc02b4db28d0fccef6bb1170d1f12.1457502970.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
tools/objtool/builtin-check.c

index fe248044415b267b0d5dcff9d447737f77ad3fb7..46a8985953909ed62ce3f71c07088d8d0fd1d00d 100644 (file)
@@ -66,9 +66,8 @@ struct objtool_file {
 const char *objname;
 static bool nofp;
 
-static struct instruction *find_instruction(struct objtool_file *file,
-                                           struct section *sec,
-                                           unsigned long offset)
+static struct instruction *find_insn(struct objtool_file *file,
+                                    struct section *sec, unsigned long offset)
 {
        struct instruction *insn;
 
@@ -79,6 +78,31 @@ static struct instruction *find_instruction(struct objtool_file *file,
        return NULL;
 }
 
+static struct instruction *next_insn_same_sec(struct objtool_file *file,
+                                             struct instruction *insn)
+{
+       struct instruction *next = list_next_entry(insn, list);
+
+       if (&next->list == &file->insns || next->sec != insn->sec)
+               return NULL;
+
+       return next;
+}
+
+#define for_each_insn(file, insn)                                      \
+       list_for_each_entry(insn, &file->insns, list)
+
+#define func_for_each_insn(file, func, insn)                           \
+       for (insn = find_insn(file, func->sec, func->offset);           \
+            insn && &insn->list != &file->insns &&                     \
+               insn->sec == func->sec &&                               \
+               insn->offset < func->offset + func->len;                \
+            insn = list_next_entry(insn, list))
+
+#define sec_for_each_insn_from(file, insn)                             \
+       for (; insn; insn = next_insn_same_sec(file, insn))
+
+
 /*
  * Check if the function has been manually whitelisted with the
  * STACK_FRAME_NON_STANDARD macro, or if it should be automatically whitelisted
@@ -99,16 +123,9 @@ static bool ignore_func(struct objtool_file *file, struct symbol *func)
                                return true;
 
        /* check if it has a context switching instruction */
-       insn = find_instruction(file, func->sec, func->offset);
-       if (!insn)
-               return false;
-       list_for_each_entry_from(insn, &file->insns, list) {
-               if (insn->sec != func->sec ||
-                   insn->offset >= func->offset + func->len)
-                       break;
+       func_for_each_insn(file, func, insn)
                if (insn->type == INSN_CONTEXT_SWITCH)
                        return true;
-       }
 
        return false;
 }
@@ -131,7 +148,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
                               int recursion)
 {
        int i;
-       struct instruction *insn, *func_insn;
+       struct instruction *insn;
        bool empty = true;
 
        /*
@@ -160,16 +177,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
        if (!func->sec)
                return 0;
 
-       func_insn = find_instruction(file, func->sec, func->offset);
-       if (!func_insn)
-               return 0;
-
-       insn = func_insn;
-       list_for_each_entry_from(insn, &file->insns, list) {
-               if (insn->sec != func->sec ||
-                   insn->offset >= func->offset + func->len)
-                       break;
-
+       func_for_each_insn(file, func, insn) {
                empty = false;
 
                if (insn->type == INSN_RETURN)
@@ -184,8 +192,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
         * case, the function's dead-end status depends on whether the target
         * of the sibling call returns.
         */
-       insn = func_insn;
-       list_for_each_entry_from(insn, &file->insns, list) {
+       func_for_each_insn(file, func, insn) {
                if (insn->sec != func->sec ||
                    insn->offset >= func->offset + func->len)
                        break;
@@ -294,17 +301,8 @@ static void get_ignores(struct objtool_file *file)
                        if (!ignore_func(file, func))
                                continue;
 
-                       insn = find_instruction(file, sec, func->offset);
-                       if (!insn)
-                               continue;
-
-                       list_for_each_entry_from(insn, &file->insns, list) {
-                               if (insn->sec != func->sec ||
-                                   insn->offset >= func->offset + func->len)
-                                       break;
-
+                       func_for_each_insn(file, func, insn)
                                insn->visited = true;
-                       }
                }
        }
 }
@@ -319,7 +317,7 @@ static int get_jump_destinations(struct objtool_file *file)
        struct section *dest_sec;
        unsigned long dest_off;
 
-       list_for_each_entry(insn, &file->insns, list) {
+       for_each_insn(file, insn) {
                if (insn->type != INSN_JUMP_CONDITIONAL &&
                    insn->type != INSN_JUMP_UNCONDITIONAL)
                        continue;
@@ -345,7 +343,7 @@ static int get_jump_destinations(struct objtool_file *file)
                        continue;
                }
 
-               insn->jump_dest = find_instruction(file, dest_sec, dest_off);
+               insn->jump_dest = find_insn(file, dest_sec, dest_off);
                if (!insn->jump_dest) {
 
                        /*
@@ -375,7 +373,7 @@ static int get_call_destinations(struct objtool_file *file)
        unsigned long dest_off;
        struct rela *rela;
 
-       list_for_each_entry(insn, &file->insns, list) {
+       for_each_insn(file, insn) {
                if (insn->type != INSN_CALL)
                        continue;
 
@@ -438,9 +436,8 @@ static int handle_group_alt(struct objtool_file *file,
 
        last_orig_insn = NULL;
        insn = orig_insn;
-       list_for_each_entry_from(insn, &file->insns, list) {
-               if (insn->sec != special_alt->orig_sec ||
-                   insn->offset >= special_alt->orig_off + special_alt->orig_len)
+       sec_for_each_insn_from(file, insn) {
+               if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
                        break;
 
                if (special_alt->skip_orig)
@@ -450,8 +447,7 @@ static int handle_group_alt(struct objtool_file *file,
                last_orig_insn = insn;
        }
 
-       if (list_is_last(&last_orig_insn->list, &file->insns) ||
-           list_next_entry(last_orig_insn, list)->sec != special_alt->orig_sec) {
+       if (!next_insn_same_sec(file, last_orig_insn)) {
                WARN("%s: don't know how to handle alternatives at end of section",
                     special_alt->orig_sec->name);
                return -1;
@@ -476,9 +472,8 @@ static int handle_group_alt(struct objtool_file *file,
 
        last_new_insn = NULL;
        insn = *new_insn;
-       list_for_each_entry_from(insn, &file->insns, list) {
-               if (insn->sec != special_alt->new_sec ||
-                   insn->offset >= special_alt->new_off + special_alt->new_len)
+       sec_for_each_insn_from(file, insn) {
+               if (insn->offset >= special_alt->new_off + special_alt->new_len)
                        break;
 
                last_new_insn = insn;
@@ -561,8 +556,8 @@ static int get_special_section_alts(struct objtool_file *file)
                        goto out;
                }
 
-               orig_insn = find_instruction(file, special_alt->orig_sec,
-                                            special_alt->orig_off);
+               orig_insn = find_insn(file, special_alt->orig_sec,
+                                     special_alt->orig_off);
                if (!orig_insn) {
                        WARN_FUNC("special: can't find orig instruction",
                                  special_alt->orig_sec, special_alt->orig_off);
@@ -572,8 +567,8 @@ static int get_special_section_alts(struct objtool_file *file)
 
                new_insn = NULL;
                if (!special_alt->group || special_alt->new_len) {
-                       new_insn = find_instruction(file, special_alt->new_sec,
-                                                   special_alt->new_off);
+                       new_insn = find_insn(file, special_alt->new_sec,
+                                            special_alt->new_off);
                        if (!new_insn) {
                                WARN_FUNC("special: can't find new instruction",
                                          special_alt->new_sec,
@@ -619,7 +614,7 @@ static int get_switch_alts(struct objtool_file *file)
        struct symbol *func;
        struct alternative *alt;
 
-       list_for_each_entry(insn, &file->insns, list) {
+       for_each_insn(file, insn) {
                if (insn->type != INSN_JUMP_DYNAMIC)
                        continue;
 
@@ -655,8 +650,7 @@ static int get_switch_alts(struct objtool_file *file)
                            rela->addend >= func->offset + func->len)
                                break;
 
-                       alt_insn = find_instruction(file, insn->sec,
-                                                   rela->addend);
+                       alt_insn = find_insn(file, insn->sec, rela->addend);
                        if (!alt_insn) {
                                WARN("%s: can't find instruction at %s+0x%x",
                                     rodata->rela->name, insn->sec->name,
@@ -881,9 +875,8 @@ static int validate_branch(struct objtool_file *file,
                        break;
                }
 
-               insn = list_next_entry(insn, list);
-
-               if (&insn->list == &file->insns || insn->sec != sec) {
+               insn = next_insn_same_sec(file, insn);
+               if (!insn) {
                        WARN("%s: unexpected end of section", sec->name);
                        warnings++;
                        return warnings;
@@ -934,8 +927,8 @@ static bool is_ubsan_insn(struct instruction *insn)
                        "__ubsan_handle_builtin_unreachable"));
 }
 
-static bool ignore_unreachable_insn(struct instruction *insn,
-                                   unsigned long func_end)
+static bool ignore_unreachable_insn(struct symbol *func,
+                                   struct instruction *insn)
 {
        int i;
 
@@ -961,7 +954,7 @@ static bool ignore_unreachable_insn(struct instruction *insn,
                        continue;
                }
 
-               if (insn->offset + insn->len >= func_end)
+               if (insn->offset + insn->len >= func->offset + func->len)
                        break;
                insn = list_next_entry(insn, list);
        }
@@ -974,7 +967,6 @@ static int validate_functions(struct objtool_file *file)
        struct section *sec;
        struct symbol *func;
        struct instruction *insn;
-       unsigned long func_end;
        int ret, warnings = 0;
 
        list_for_each_entry(sec, &file->elf->sections, list) {
@@ -982,7 +974,7 @@ static int validate_functions(struct objtool_file *file)
                        if (func->type != STT_FUNC)
                                continue;
 
-                       insn = find_instruction(file, sec, func->offset);
+                       insn = find_insn(file, sec, func->offset);
                        if (!insn) {
                                WARN("%s(): can't find starting instruction",
                                     func->name);
@@ -1000,21 +992,11 @@ static int validate_functions(struct objtool_file *file)
                        if (func->type != STT_FUNC)
                                continue;
 
-                       insn = find_instruction(file, sec, func->offset);
-                       if (!insn)
-                               continue;
-
-                       func_end = func->offset + func->len;
-
-                       list_for_each_entry_from(insn, &file->insns, list) {
-                               if (insn->sec != func->sec ||
-                                   insn->offset >= func_end)
-                                       break;
-
+                       func_for_each_insn(file, func, insn) {
                                if (insn->visited)
                                        continue;
 
-                               if (!ignore_unreachable_insn(insn, func_end)) {
+                               if (!ignore_unreachable_insn(func, insn)) {
                                        WARN_FUNC("function has unreachable instruction", insn->sec, insn->offset);
                                        warnings++;
                                }
@@ -1032,7 +1014,7 @@ static int validate_uncallable_instructions(struct objtool_file *file)
        struct instruction *insn;
        int warnings = 0;
 
-       list_for_each_entry(insn, &file->insns, list) {
+       for_each_insn(file, insn) {
                if (!insn->visited && insn->type == INSN_RETURN) {
                        WARN_FUNC("return instruction outside of a callable function",
                                  insn->sec, insn->offset);