objtool: Add helper macros for traversing instructions

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>
This commit is contained in:
Josh Poimboeuf 2016-03-09 00:06:55 -06:00 committed by Ingo Molnar
parent d8d1b2cb58
commit 74aec058be
1 changed files with 55 additions and 73 deletions

View File

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