mirror of https://github.com/torvalds/linux.git
objtool: Refactor add_jump_destinations()
The add_jump_destinations() logic is a bit weird and convoluted after being incrementally tweaked over the years. Refactor it to hopefully be more logical and straightforward. Acked-by: Petr Mladek <pmladek@suse.com> Tested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
This commit is contained in:
parent
935c0b6a05
commit
a05de0a772
|
|
@ -1423,9 +1423,14 @@ static void add_return_call(struct objtool_file *file, struct instruction *insn,
|
|||
}
|
||||
|
||||
static bool is_first_func_insn(struct objtool_file *file,
|
||||
struct instruction *insn, struct symbol *sym)
|
||||
struct instruction *insn)
|
||||
{
|
||||
if (insn->offset == sym->offset)
|
||||
struct symbol *func = insn_func(insn);
|
||||
|
||||
if (!func)
|
||||
return false;
|
||||
|
||||
if (insn->offset == func->offset)
|
||||
return true;
|
||||
|
||||
/* Allow direct CALL/JMP past ENDBR */
|
||||
|
|
@ -1433,51 +1438,30 @@ static bool is_first_func_insn(struct objtool_file *file,
|
|||
struct instruction *prev = prev_insn_same_sym(file, insn);
|
||||
|
||||
if (prev && prev->type == INSN_ENDBR &&
|
||||
insn->offset == sym->offset + prev->len)
|
||||
insn->offset == func->offset + prev->len)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* A sibling call is a tail-call to another symbol -- to differentiate from a
|
||||
* recursive tail-call which is to the same symbol.
|
||||
*/
|
||||
static bool jump_is_sibling_call(struct objtool_file *file,
|
||||
struct instruction *from, struct instruction *to)
|
||||
{
|
||||
struct symbol *fs = from->sym;
|
||||
struct symbol *ts = to->sym;
|
||||
|
||||
/* Not a sibling call if from/to a symbol hole */
|
||||
if (!fs || !ts)
|
||||
return false;
|
||||
|
||||
/* Not a sibling call if not targeting the start of a symbol. */
|
||||
if (!is_first_func_insn(file, to, ts))
|
||||
return false;
|
||||
|
||||
/* Disallow sibling calls into STT_NOTYPE */
|
||||
if (is_notype_sym(ts))
|
||||
return false;
|
||||
|
||||
/* Must not be self to be a sibling */
|
||||
return fs->pfunc != ts->pfunc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the destination instructions for all jumps.
|
||||
*/
|
||||
static int add_jump_destinations(struct objtool_file *file)
|
||||
{
|
||||
struct instruction *insn, *jump_dest;
|
||||
struct instruction *insn;
|
||||
struct reloc *reloc;
|
||||
struct section *dest_sec;
|
||||
unsigned long dest_off;
|
||||
|
||||
for_each_insn(file, insn) {
|
||||
struct symbol *func = insn_func(insn);
|
||||
struct instruction *dest_insn;
|
||||
struct section *dest_sec;
|
||||
struct symbol *dest_sym;
|
||||
unsigned long dest_off;
|
||||
|
||||
if (!is_static_jump(insn))
|
||||
continue;
|
||||
|
||||
if (insn->jump_dest) {
|
||||
/*
|
||||
|
|
@ -1486,51 +1470,53 @@ static int add_jump_destinations(struct objtool_file *file)
|
|||
*/
|
||||
continue;
|
||||
}
|
||||
if (!is_static_jump(insn))
|
||||
continue;
|
||||
|
||||
reloc = insn_reloc(file, insn);
|
||||
if (!reloc) {
|
||||
dest_sec = insn->sec;
|
||||
dest_off = arch_jump_destination(insn);
|
||||
} else if (is_sec_sym(reloc->sym)) {
|
||||
dest_sec = reloc->sym->sec;
|
||||
dest_off = arch_insn_adjusted_addend(insn, reloc);
|
||||
} else if (reloc->sym->retpoline_thunk) {
|
||||
if (add_retpoline_call(file, insn))
|
||||
return -1;
|
||||
continue;
|
||||
} else if (reloc->sym->return_thunk) {
|
||||
add_return_call(file, insn, true);
|
||||
continue;
|
||||
} else if (func) {
|
||||
/*
|
||||
* External sibling call or internal sibling call with
|
||||
* STT_FUNC reloc.
|
||||
*/
|
||||
if (add_call_dest(file, insn, reloc->sym, true))
|
||||
return -1;
|
||||
continue;
|
||||
} else if (reloc->sym->sec->idx) {
|
||||
dest_sec = reloc->sym->sec;
|
||||
dest_off = reloc->sym->sym.st_value +
|
||||
arch_insn_adjusted_addend(insn, reloc);
|
||||
dest_sym = dest_sec->sym;
|
||||
} else {
|
||||
/* non-func asm code jumping to another file */
|
||||
continue;
|
||||
dest_sym = reloc->sym;
|
||||
if (is_undef_sym(dest_sym)) {
|
||||
if (dest_sym->retpoline_thunk) {
|
||||
if (add_retpoline_call(file, insn))
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dest_sym->return_thunk) {
|
||||
add_return_call(file, insn, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* External symbol */
|
||||
if (func) {
|
||||
/* External sibling call */
|
||||
if (add_call_dest(file, insn, dest_sym, true))
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Non-func asm code jumping to external symbol */
|
||||
continue;
|
||||
}
|
||||
|
||||
dest_sec = dest_sym->sec;
|
||||
dest_off = dest_sym->offset + arch_insn_adjusted_addend(insn, reloc);
|
||||
}
|
||||
|
||||
jump_dest = find_insn(file, dest_sec, dest_off);
|
||||
if (!jump_dest) {
|
||||
dest_insn = find_insn(file, dest_sec, dest_off);
|
||||
if (!dest_insn) {
|
||||
struct symbol *sym = find_symbol_by_offset(dest_sec, dest_off);
|
||||
|
||||
/*
|
||||
* This is a special case for retbleed_untrain_ret().
|
||||
* It jumps to __x86_return_thunk(), but objtool
|
||||
* can't find the thunk's starting RET
|
||||
* instruction, because the RET is also in the
|
||||
* middle of another instruction. Objtool only
|
||||
* knows about the outer instruction.
|
||||
* retbleed_untrain_ret() jumps to
|
||||
* __x86_return_thunk(), but objtool can't find
|
||||
* the thunk's starting RET instruction,
|
||||
* because the RET is also in the middle of
|
||||
* another instruction. Objtool only knows
|
||||
* about the outer instruction.
|
||||
*/
|
||||
if (sym && sym->embedded_insn) {
|
||||
add_return_call(file, insn, false);
|
||||
|
|
@ -1538,73 +1524,73 @@ static int add_jump_destinations(struct objtool_file *file)
|
|||
}
|
||||
|
||||
/*
|
||||
* GCOV/KCOV dead code can jump to the end of the
|
||||
* function/section.
|
||||
* GCOV/KCOV dead code can jump to the end of
|
||||
* the function/section.
|
||||
*/
|
||||
if (file->ignore_unreachables && func &&
|
||||
dest_sec == insn->sec &&
|
||||
dest_off == func->offset + func->len)
|
||||
continue;
|
||||
|
||||
ERROR_INSN(insn, "can't find jump dest instruction at %s+0x%lx",
|
||||
dest_sec->name, dest_off);
|
||||
ERROR_INSN(insn, "can't find jump dest instruction at %s",
|
||||
offstr(dest_sec, dest_off));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* An intra-TU jump in retpoline.o might not have a relocation
|
||||
* for its jump dest, in which case the above
|
||||
* add_{retpoline,return}_call() didn't happen.
|
||||
*/
|
||||
if (jump_dest->sym && jump_dest->offset == jump_dest->sym->offset) {
|
||||
if (jump_dest->sym->retpoline_thunk) {
|
||||
if (add_retpoline_call(file, insn))
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
if (jump_dest->sym->return_thunk) {
|
||||
add_return_call(file, insn, true);
|
||||
continue;
|
||||
}
|
||||
if (!dest_sym || is_sec_sym(dest_sym)) {
|
||||
dest_sym = dest_insn->sym;
|
||||
if (!dest_sym)
|
||||
goto set_jump_dest;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cross-function jump.
|
||||
*/
|
||||
|
||||
if (func && insn_func(jump_dest) && !func->cold &&
|
||||
insn_func(jump_dest)->cold) {
|
||||
|
||||
/*
|
||||
* For GCC 8+, create parent/child links for any cold
|
||||
* subfunctions. This is _mostly_ redundant with a
|
||||
* similar initialization in read_symbols().
|
||||
*
|
||||
* If a function has aliases, we want the *first* such
|
||||
* function in the symbol table to be the subfunction's
|
||||
* parent. In that case we overwrite the
|
||||
* initialization done in read_symbols().
|
||||
*
|
||||
* However this code can't completely replace the
|
||||
* read_symbols() code because this doesn't detect the
|
||||
* case where the parent function's only reference to a
|
||||
* subfunction is through a jump table.
|
||||
*/
|
||||
func->cfunc = insn_func(jump_dest);
|
||||
insn_func(jump_dest)->pfunc = func;
|
||||
}
|
||||
|
||||
if (jump_is_sibling_call(file, insn, jump_dest)) {
|
||||
/*
|
||||
* Internal sibling call without reloc or with
|
||||
* STT_SECTION reloc.
|
||||
*/
|
||||
if (add_call_dest(file, insn, insn_func(jump_dest), true))
|
||||
if (dest_sym->retpoline_thunk && dest_insn->offset == dest_sym->offset) {
|
||||
if (add_retpoline_call(file, insn))
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
insn->jump_dest = jump_dest;
|
||||
if (dest_sym->return_thunk && dest_insn->offset == dest_sym->offset) {
|
||||
add_return_call(file, insn, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!insn->sym || insn->sym == dest_insn->sym)
|
||||
goto set_jump_dest;
|
||||
|
||||
/*
|
||||
* Internal cross-function jump.
|
||||
*/
|
||||
|
||||
/*
|
||||
* For GCC 8+, create parent/child links for any cold
|
||||
* subfunctions. This is _mostly_ redundant with a
|
||||
* similar initialization in read_symbols().
|
||||
*
|
||||
* If a function has aliases, we want the *first* such
|
||||
* function in the symbol table to be the subfunction's
|
||||
* parent. In that case we overwrite the
|
||||
* initialization done in read_symbols().
|
||||
*
|
||||
* However this code can't completely replace the
|
||||
* read_symbols() code because this doesn't detect the
|
||||
* case where the parent function's only reference to a
|
||||
* subfunction is through a jump table.
|
||||
*/
|
||||
if (func && dest_sym->cold) {
|
||||
func->cfunc = dest_sym;
|
||||
dest_sym->pfunc = func;
|
||||
goto set_jump_dest;
|
||||
}
|
||||
|
||||
if (is_first_func_insn(file, dest_insn)) {
|
||||
/* Internal sibling call */
|
||||
if (add_call_dest(file, insn, dest_sym, true))
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
set_jump_dest:
|
||||
insn->jump_dest = dest_insn;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -181,9 +181,9 @@ static inline unsigned int elf_text_rela_type(struct elf *elf)
|
|||
return elf_addr_size(elf) == 4 ? R_TEXT32 : R_TEXT64;
|
||||
}
|
||||
|
||||
static inline bool sym_has_sec(struct symbol *sym)
|
||||
static inline bool is_undef_sym(struct symbol *sym)
|
||||
{
|
||||
return sym->sec->idx;
|
||||
return !sym->sec->idx;
|
||||
}
|
||||
|
||||
static inline bool is_null_sym(struct symbol *sym)
|
||||
|
|
|
|||
Loading…
Reference in New Issue