[PATCH 05/11] objtool: Add helper macros for traversing instructions

From: Josh Poimboeuf
Date: Wed Mar 09 2016 - 01:08:36 EST


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@xxxxxxxxxx>
---
tools/objtool/builtin-check.c | 128 ++++++++++++++++++------------------------
1 file changed, 55 insertions(+), 73 deletions(-)

diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index fe24804..46a8985 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -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);
--
2.4.3