Commit 7acfe531 authored by Josh Poimboeuf's avatar Josh Poimboeuf Committed by Borislav Petkov

objtool: Improve call destination function detection

A recent clang change, combined with a binutils bug, can trigger a
situation where a ".Lprintk$local" STT_NOTYPE symbol gets created at the
same offset as the "printk" STT_FUNC symbol.  This confuses objtool:

  kernel/printk/printk.o: warning: objtool: ignore_loglevel_setup()+0x10: can't find call dest symbol at .text+0xc67

Improve the call destination detection by looking specifically for an
STT_FUNC symbol.
Reported-by: default avatarNick Desaulniers <ndesaulniers@google.com>
Signed-off-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Tested-by: default avatarNick Desaulniers <ndesaulniers@google.com>
Tested-by: default avatarNathan Chancellor <natechancellor@gmail.com>
Link: https://github.com/ClangBuiltLinux/linux/issues/872
Link: https://sourceware.org/bugzilla/show_bug.cgi?id=25551
Link: https://lkml.kernel.org/r/0a7ee320bc0ea4469bd3dc450a7b4725669e0ea9.1581997059.git.jpoimboe@redhat.com
parent 113d4bc9
...@@ -420,8 +420,8 @@ static void add_ignores(struct objtool_file *file) ...@@ -420,8 +420,8 @@ static void add_ignores(struct objtool_file *file)
break; break;
case STT_SECTION: case STT_SECTION:
func = find_symbol_by_offset(rela->sym->sec, rela->addend); func = find_func_by_offset(rela->sym->sec, rela->addend);
if (!func || func->type != STT_FUNC) if (!func)
continue; continue;
break; break;
...@@ -665,10 +665,14 @@ static int add_call_destinations(struct objtool_file *file) ...@@ -665,10 +665,14 @@ static int add_call_destinations(struct objtool_file *file)
insn->len); insn->len);
if (!rela) { if (!rela) {
dest_off = insn->offset + insn->len + insn->immediate; dest_off = insn->offset + insn->len + insn->immediate;
insn->call_dest = find_symbol_by_offset(insn->sec, insn->call_dest = find_func_by_offset(insn->sec, dest_off);
dest_off); if (!insn->call_dest)
insn->call_dest = find_symbol_by_offset(insn->sec, dest_off);
if (!insn->call_dest && !insn->ignore) { if (insn->ignore)
continue;
if (!insn->call_dest) {
WARN_FUNC("unsupported intra-function call", WARN_FUNC("unsupported intra-function call",
insn->sec, insn->offset); insn->sec, insn->offset);
if (retpoline) if (retpoline)
...@@ -676,11 +680,16 @@ static int add_call_destinations(struct objtool_file *file) ...@@ -676,11 +680,16 @@ static int add_call_destinations(struct objtool_file *file)
return -1; return -1;
} }
if (insn->func && insn->call_dest->type != STT_FUNC) {
WARN_FUNC("unsupported call to non-function",
insn->sec, insn->offset);
return -1;
}
} else if (rela->sym->type == STT_SECTION) { } else if (rela->sym->type == STT_SECTION) {
insn->call_dest = find_symbol_by_offset(rela->sym->sec, insn->call_dest = find_func_by_offset(rela->sym->sec,
rela->addend+4); rela->addend+4);
if (!insn->call_dest || if (!insn->call_dest) {
insn->call_dest->type != STT_FUNC) {
WARN_FUNC("can't find call dest symbol at %s+0x%x", WARN_FUNC("can't find call dest symbol at %s+0x%x",
insn->sec, insn->offset, insn->sec, insn->offset,
rela->sym->sec->name, rela->sym->sec->name,
......
...@@ -62,8 +62,18 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) ...@@ -62,8 +62,18 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
struct symbol *sym; struct symbol *sym;
list_for_each_entry(sym, &sec->symbol_list, list) list_for_each_entry(sym, &sec->symbol_list, list)
if (sym->type != STT_SECTION && if (sym->type != STT_SECTION && sym->offset == offset)
sym->offset == offset) return sym;
return NULL;
}
struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)
{
struct symbol *sym;
list_for_each_entry(sym, &sec->symbol_list, list)
if (sym->type == STT_FUNC && sym->offset == offset)
return sym; return sym;
return NULL; return NULL;
......
...@@ -77,6 +77,7 @@ struct elf { ...@@ -77,6 +77,7 @@ struct elf {
struct elf *elf_read(const char *name, int flags); struct elf *elf_read(const char *name, int flags);
struct section *find_section_by_name(struct elf *elf, const char *name); struct section *find_section_by_name(struct elf *elf, const char *name);
struct symbol *find_func_by_offset(struct section *sec, unsigned long offset);
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
struct symbol *find_symbol_by_name(struct elf *elf, const char *name); struct symbol *find_symbol_by_name(struct elf *elf, const char *name);
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset); struct symbol *find_symbol_containing(struct section *sec, unsigned long offset);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment