From 644592d328370af4b3e027b7b1ae9f81613782d8 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 10 Feb 2020 12:32:38 -0600 Subject: objtool: Fail the kernel build on fatal errors When objtool encounters a fatal error, it usually means the binary is corrupt or otherwise broken in some way. Up until now, such errors were just treated as warnings which didn't fail the kernel build. However, objtool is now stable enough that if a fatal error is discovered, it most likely means something is seriously wrong and it should fail the kernel build. Note that this doesn't apply to "normal" objtool warnings; only fatal ones. Suggested-by: Borislav Petkov Signed-off-by: Josh Poimboeuf Signed-off-by: Borislav Petkov Reviewed-by: Julien Thierry Link: https://lkml.kernel.org/r/f18c3743de0fef673d49dd35760f26bdef7f6fc3.1581359535.git.jpoimboe@redhat.com --- tools/objtool/check.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 4768d91c6d68..796f6a172efd 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -2491,8 +2491,14 @@ int check(const char *_objname, bool orc) out: cleanup(&file); - /* ignore warnings for now until we get all the code cleaned up */ - if (ret || warnings) - return 0; + if (ret < 0) { + /* + * Fatal error. The binary is corrupt or otherwise broken in + * some way, or objtool itself is broken. Fail the kernel + * build. + */ + return ret; + } + return 0; } -- cgit v1.2.3 From a22961409c02b93ffa7ed78f67fb57a1ba6c787d Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 10 Feb 2020 12:32:39 -0600 Subject: objtool: Add is_static_jump() helper There are several places where objtool tests for a non-dynamic (aka direct) jump. Move the check to a helper function. Signed-off-by: Josh Poimboeuf Signed-off-by: Borislav Petkov Reviewed-by: Julien Thierry Link: https://lkml.kernel.org/r/9b8b438df918276315e4765c60d2587f3c7ad698.1581359535.git.jpoimboe@redhat.com --- tools/objtool/check.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 796f6a172efd..9016ae1c0c55 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -97,14 +97,19 @@ static struct instruction *next_insn_same_func(struct objtool_file *file, for (insn = next_insn_same_sec(file, insn); insn; \ insn = next_insn_same_sec(file, insn)) +static bool is_static_jump(struct instruction *insn) +{ + return insn->type == INSN_JUMP_CONDITIONAL || + insn->type == INSN_JUMP_UNCONDITIONAL; +} + static bool is_sibling_call(struct instruction *insn) { /* An indirect jump is either a sibling call or a jump to a table. */ if (insn->type == INSN_JUMP_DYNAMIC) return list_empty(&insn->alts); - if (insn->type != INSN_JUMP_CONDITIONAL && - insn->type != INSN_JUMP_UNCONDITIONAL) + if (!is_static_jump(insn)) return false; /* add_jump_destinations() sets insn->call_dest for sibling calls. */ @@ -553,8 +558,7 @@ static int add_jump_destinations(struct objtool_file *file) unsigned long dest_off; for_each_insn(file, insn) { - if (insn->type != INSN_JUMP_CONDITIONAL && - insn->type != INSN_JUMP_UNCONDITIONAL) + if (!is_static_jump(insn)) continue; if (insn->ignore || insn->offset == FAKE_JUMP_OFFSET) @@ -764,8 +768,7 @@ static int handle_group_alt(struct objtool_file *file, insn->ignore = orig_insn->ignore_alts; insn->func = orig_insn->func; - if (insn->type != INSN_JUMP_CONDITIONAL && - insn->type != INSN_JUMP_UNCONDITIONAL) + if (!is_static_jump(insn)) continue; if (!insn->immediate) -- cgit v1.2.3 From dc4197236c20e761f2007c641afd193f81a00a74 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 10 Feb 2020 12:32:40 -0600 Subject: objtool: Add relocation check for alternative sections Relocations in alternative code can be dangerous, because the code is copy/pasted to the text section after relocations have been resolved, which can corrupt PC-relative addresses. However, relocations might be acceptable in some cases, depending on the architecture. For example, the x86 alternatives code manually fixes up the target addresses for PC-relative jumps and calls. So disallow relocations in alternative code, except where the x86 arch code allows it. This code may need to be tweaked for other arches when objtool gets support for them. Suggested-by: Linus Torvalds Signed-off-by: Josh Poimboeuf Signed-off-by: Borislav Petkov Reviewed-by: Julien Thierry Link: https://lkml.kernel.org/r/7b90b68d093311e4e8f6b504a9e1c758fd7e0002.1581359535.git.jpoimboe@redhat.com --- tools/objtool/check.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 9016ae1c0c55..b038de2ccd71 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -768,6 +768,27 @@ static int handle_group_alt(struct objtool_file *file, insn->ignore = orig_insn->ignore_alts; insn->func = orig_insn->func; + /* + * Since alternative replacement code is copy/pasted by the + * kernel after applying relocations, generally such code can't + * have relative-address relocation references to outside the + * .altinstr_replacement section, unless the arch's + * alternatives code can adjust the relative offsets + * accordingly. + * + * The x86 alternatives code adjusts the offsets only when it + * encounters a branch instruction at the very beginning of the + * replacement group. + */ + if ((insn->offset != special_alt->new_off || + (insn->type != INSN_CALL && !is_static_jump(insn))) && + find_rela_by_dest_range(insn->sec, insn->offset, insn->len)) { + + WARN_FUNC("unsupported relocation in alternatives section", + insn->sec, insn->offset); + return -1; + } + if (!is_static_jump(insn)) continue; -- cgit v1.2.3 From 113d4bc9048336ba7c3d2ad972dbad4aef6e148a Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 17 Feb 2020 21:41:53 -0600 Subject: objtool: Fix clang switch table edge case Clang has the ability to create a switch table which is not a jump table, but is rather a table of string pointers. This confuses objtool, because it sees the relocations for the string pointers and assumes they're part of a jump table: drivers/ata/sata_dwc_460ex.o: warning: objtool: sata_dwc_bmdma_start_by_tag()+0x3a2: can't find switch jump table net/ceph/messenger.o: warning: objtool: ceph_con_workfn()+0x47c: can't find switch jump table Make objtool's find_jump_table() smart enough to distinguish between a switch jump table (which has relocations to text addresses in the same function as the original instruction) and other anonymous rodata (which may have relocations to elsewhere). Reported-by: Nick Desaulniers Signed-off-by: Josh Poimboeuf Signed-off-by: Borislav Petkov Tested-by: Nick Desaulniers Tested-by: Nathan Chancellor Link: https://github.com/ClangBuiltLinux/linux/issues/485 Link: https://lkml.kernel.org/r/263f6aae46d33da0b86d7030ced878cb5cab1788.1581997059.git.jpoimboe@redhat.com --- tools/objtool/check.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index b038de2ccd71..4d6e283951ec 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1025,7 +1025,7 @@ static struct rela *find_jump_table(struct objtool_file *file, struct instruction *insn) { struct rela *text_rela, *table_rela; - struct instruction *orig_insn = insn; + struct instruction *dest_insn, *orig_insn = insn; struct section *table_sec; unsigned long table_offset; @@ -1077,10 +1077,17 @@ static struct rela *find_jump_table(struct objtool_file *file, strcmp(table_sec->name, C_JUMP_TABLE_SECTION)) continue; - /* Each table entry has a rela associated with it. */ + /* + * Each table entry has a rela associated with it. The rela + * should reference text in the same function as the original + * instruction. + */ table_rela = find_rela_by_dest(table_sec, table_offset); if (!table_rela) continue; + dest_insn = find_insn(file, table_rela->sym->sec, table_rela->addend); + if (!dest_insn || !dest_insn->func || dest_insn->func->pfunc != func) + continue; /* * Use of RIP-relative switch jumps is quite rare, and -- cgit v1.2.3 From 7acfe5315312fc56c2a94c9216448087b38ae909 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 17 Feb 2020 21:41:54 -0600 Subject: 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: Nick Desaulniers Signed-off-by: Josh Poimboeuf Signed-off-by: Borislav Petkov Tested-by: Nick Desaulniers Tested-by: Nathan Chancellor 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 --- tools/objtool/check.c | 27 ++++++++++++++++++--------- tools/objtool/elf.c | 14 ++++++++++++-- tools/objtool/elf.h | 1 + 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 4d6e283951ec..6b6178ef4464 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -420,8 +420,8 @@ static void add_ignores(struct objtool_file *file) break; case STT_SECTION: - func = find_symbol_by_offset(rela->sym->sec, rela->addend); - if (!func || func->type != STT_FUNC) + func = find_func_by_offset(rela->sym->sec, rela->addend); + if (!func) continue; break; @@ -665,10 +665,14 @@ static int add_call_destinations(struct objtool_file *file) insn->len); if (!rela) { dest_off = insn->offset + insn->len + insn->immediate; - insn->call_dest = find_symbol_by_offset(insn->sec, - dest_off); + insn->call_dest = find_func_by_offset(insn->sec, 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", insn->sec, insn->offset); if (retpoline) @@ -676,11 +680,16 @@ static int add_call_destinations(struct objtool_file *file) 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) { - insn->call_dest = find_symbol_by_offset(rela->sym->sec, - rela->addend+4); - if (!insn->call_dest || - insn->call_dest->type != STT_FUNC) { + insn->call_dest = find_func_by_offset(rela->sym->sec, + rela->addend+4); + if (!insn->call_dest) { WARN_FUNC("can't find call dest symbol at %s+0x%x", insn->sec, insn->offset, rela->sym->sec->name, diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index edba4745f25a..cc4601c879ce 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -62,8 +62,18 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) struct symbol *sym; list_for_each_entry(sym, &sec->symbol_list, list) - if (sym->type != STT_SECTION && - sym->offset == offset) + if (sym->type != STT_SECTION && 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 NULL; diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h index 44150204db4d..a1963259b930 100644 --- a/tools/objtool/elf.h +++ b/tools/objtool/elf.h @@ -77,6 +77,7 @@ struct elf { struct elf *elf_read(const char *name, int flags); 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_name(struct elf *elf, const char *name); struct symbol *find_symbol_containing(struct section *sec, unsigned long offset); -- cgit v1.2.3 From a92e92d1a749e9bae9828f34f632d56ac2c6d2c3 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 10 Mar 2020 18:07:44 +0100 Subject: objtool: Introduce validate_return() Trivial 'cleanup' to save one indentation level and match validate_call(). Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160923.963996225@infradead.org --- tools/objtool/check.c | 64 +++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 6b6178ef4464..da17b5acc504 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1975,6 +1975,41 @@ static int validate_sibling_call(struct instruction *insn, struct insn_state *st return validate_call(insn, state); } +static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state) +{ + if (state->uaccess && !func_uaccess_safe(func)) { + WARN_FUNC("return with UACCESS enabled", + insn->sec, insn->offset); + return 1; + } + + if (!state->uaccess && func_uaccess_safe(func)) { + WARN_FUNC("return with UACCESS disabled from a UACCESS-safe function", + insn->sec, insn->offset); + return 1; + } + + if (state->df) { + WARN_FUNC("return with DF set", + insn->sec, insn->offset); + return 1; + } + + if (func && has_modified_stack_frame(state)) { + WARN_FUNC("return with modified stack frame", + insn->sec, insn->offset); + return 1; + } + + if (state->bp_scratch) { + WARN("%s uses BP as a scratch register", + func->name); + return 1; + } + + return 0; +} + /* * Follow the branch starting at the given instruction, and recursively follow * any other branches (jumps). Meanwhile, track the frame pointer state at @@ -2090,34 +2125,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, switch (insn->type) { case INSN_RETURN: - if (state.uaccess && !func_uaccess_safe(func)) { - WARN_FUNC("return with UACCESS enabled", sec, insn->offset); - return 1; - } - - if (!state.uaccess && func_uaccess_safe(func)) { - WARN_FUNC("return with UACCESS disabled from a UACCESS-safe function", sec, insn->offset); - return 1; - } - - if (state.df) { - WARN_FUNC("return with DF set", sec, insn->offset); - return 1; - } - - if (func && has_modified_stack_frame(&state)) { - WARN_FUNC("return with modified stack frame", - sec, insn->offset); - return 1; - } - - if (state.bp_scratch) { - WARN("%s uses BP as a scratch register", - func->name); - return 1; - } - - return 0; + return validate_return(func, insn, &state); case INSN_CALL: case INSN_CALL_DYNAMIC: -- cgit v1.2.3 From dbf4aeb0a494020644507459c2446e632cba1a05 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 10 Mar 2020 18:24:59 +0100 Subject: objtool: Rename func_for_each_insn() There is func_for_each_insn() and func_for_each_insn_all(), the both iterate the instructions, but the first uses symbol offset/length while the second uses insn->func. Rename func_for_each_insn() to sym_for_eac_insn() because it iterates on symbol information. Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.024341229@infradead.org --- tools/objtool/check.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index da17b5acc504..564ea1dcc85e 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -77,17 +77,17 @@ static struct instruction *next_insn_same_func(struct objtool_file *file, insn; \ insn = next_insn_same_func(file, insn)) -#define func_for_each_insn(file, func, insn) \ - for (insn = find_insn(file, func->sec, func->offset); \ +#define sym_for_each_insn(file, sym, insn) \ + for (insn = find_insn(file, sym->sec, sym->offset); \ insn && &insn->list != &file->insn_list && \ - insn->sec == func->sec && \ - insn->offset < func->offset + func->len; \ + insn->sec == sym->sec && \ + insn->offset < sym->offset + sym->len; \ insn = list_next_entry(insn, list)) -#define func_for_each_insn_continue_reverse(file, func, insn) \ +#define sym_for_each_insn_continue_reverse(file, sym, insn) \ for (insn = list_prev_entry(insn, list); \ &insn->list != &file->insn_list && \ - insn->sec == func->sec && insn->offset >= func->offset; \ + insn->sec == sym->sec && insn->offset >= sym->offset; \ insn = list_prev_entry(insn, list)) #define sec_for_each_insn_from(file, insn) \ @@ -286,7 +286,7 @@ static int decode_instructions(struct objtool_file *file) return -1; } - func_for_each_insn(file, func, insn) + sym_for_each_insn(file, func, insn) insn->func = func; } } @@ -2064,7 +2064,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, i = insn; save_insn = NULL; - func_for_each_insn_continue_reverse(file, func, i) { + sym_for_each_insn_continue_reverse(file, func, i) { if (i->save) { save_insn = i; break; -- cgit v1.2.3 From f0f70adb78108a0cbc321a07133cd78ea4f84699 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 10 Mar 2020 18:27:24 +0100 Subject: objtool: Rename func_for_each_insn_all() Now that func_for_each_insn() is available, rename func_for_each_insn_all(). This gets us: sym_for_each_insn() - iterate on symbol offset/len func_for_each_insn() - iterate on insn->func Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.083720147@infradead.org --- tools/objtool/check.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 564ea1dcc85e..43f7d3c2e8b2 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -72,7 +72,7 @@ static struct instruction *next_insn_same_func(struct objtool_file *file, return find_insn(file, func->cfunc->sec, func->cfunc->offset); } -#define func_for_each_insn_all(file, func, insn) \ +#define func_for_each_insn(file, func, insn) \ for (insn = find_insn(file, func->sec, func->offset); \ insn; \ insn = next_insn_same_func(file, insn)) @@ -170,7 +170,7 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func, if (!insn->func) return false; - func_for_each_insn_all(file, func, insn) { + func_for_each_insn(file, func, insn) { empty = false; if (insn->type == INSN_RETURN) @@ -185,7 +185,7 @@ static bool __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. */ - func_for_each_insn_all(file, func, insn) { + func_for_each_insn(file, func, insn) { if (is_sibling_call(insn)) { struct instruction *dest = insn->jump_dest; @@ -430,7 +430,7 @@ static void add_ignores(struct objtool_file *file) continue; } - func_for_each_insn_all(file, func, insn) + func_for_each_insn(file, func, insn) insn->ignore = true; } } @@ -1122,7 +1122,7 @@ static void mark_func_jump_tables(struct objtool_file *file, struct instruction *insn, *last = NULL; struct rela *rela; - func_for_each_insn_all(file, func, insn) { + func_for_each_insn(file, func, insn) { if (!last) last = insn; @@ -1157,7 +1157,7 @@ static int add_func_jump_tables(struct objtool_file *file, struct instruction *insn; int ret; - func_for_each_insn_all(file, func, insn) { + func_for_each_insn(file, func, insn) { if (!insn->jump_table) continue; -- cgit v1.2.3 From fc8bd77d6476d7733ace9e03093b4acaee6e0605 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 16 Mar 2020 10:13:45 +0100 Subject: x86/kexec: Use RIP relative addressing Normally identity_mapped is not visible to objtool, due to: arch/x86/kernel/Makefile:OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o := y However, when we want to run objtool on vmlinux.o there is no hiding it: vmlinux.o: warning: objtool: .text+0x4c0f1: unsupported intra-function call Replace the (i386 inspired) pattern: call 1f 1: popq %r8 subq $(1b - relocate_kernel), %r8 With a x86_64 RIP-relative LEA: leaq relocate_kernel(%rip), %r8 Suggested-by: Brian Gerst Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.143334345@infradead.org --- arch/x86/kernel/relocate_kernel_64.S | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S index ef3ba99068d3..cc5c8b9048b5 100644 --- a/arch/x86/kernel/relocate_kernel_64.S +++ b/arch/x86/kernel/relocate_kernel_64.S @@ -196,10 +196,7 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped) /* get the re-entry point of the peer system */ movq 0(%rsp), %rbp - call 1f -1: - popq %r8 - subq $(1b - relocate_kernel), %r8 + leaq relocate_kernel(%rip), %r8 movq CP_PA_SWAP_PAGE(%r8), %r10 movq CP_PA_BACKUP_PAGES_MAP(%r8), %rdi movq CP_PA_TABLE_PAGE(%r8), %rax -- cgit v1.2.3 From 36cc552055a5f95bab479533b4ebbad6a6cea0e1 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 24 Mar 2020 15:35:42 +0100 Subject: x86/kexec: Make relocate_kernel_64.S objtool clean Having fixed the biggest objtool issue in this file; fix up the rest and remove the exception. Suggested-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.202621656@infradead.org --- arch/x86/kernel/Makefile | 1 - arch/x86/kernel/relocate_kernel_64.S | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 9b294c13809a..8be5926cce51 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -28,7 +28,6 @@ KASAN_SANITIZE_dumpstack_$(BITS).o := n KASAN_SANITIZE_stacktrace.o := n KASAN_SANITIZE_paravirt.o := n -OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o := y OBJECT_FILES_NON_STANDARD_test_nx.o := y OBJECT_FILES_NON_STANDARD_paravirt_patch.o := y diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S index cc5c8b9048b5..a4d9a261425b 100644 --- a/arch/x86/kernel/relocate_kernel_64.S +++ b/arch/x86/kernel/relocate_kernel_64.S @@ -9,6 +9,8 @@ #include #include #include +#include +#include /* * Must be relocatable PIC code callable as a C function @@ -39,6 +41,7 @@ .align PAGE_SIZE .code64 SYM_CODE_START_NOALIGN(relocate_kernel) + UNWIND_HINT_EMPTY /* * %rdi indirection_page * %rsi page_list @@ -105,6 +108,7 @@ SYM_CODE_START_NOALIGN(relocate_kernel) SYM_CODE_END(relocate_kernel) SYM_CODE_START_LOCAL_NOALIGN(identity_mapped) + UNWIND_HINT_EMPTY /* set return address to 0 if not preserving context */ pushq $0 /* store the start address on the stack */ @@ -192,6 +196,7 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped) 1: popq %rdx leaq PAGE_SIZE(%r10), %rsp + ANNOTATE_RETPOLINE_SAFE call *%rdx /* get the re-entry point of the peer system */ @@ -209,6 +214,7 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped) SYM_CODE_END(identity_mapped) SYM_CODE_START_LOCAL_NOALIGN(virtual_mapped) + UNWIND_HINT_EMPTY movq RSP(%r8), %rsp movq CR4(%r8), %rax movq %rax, %cr4 @@ -230,6 +236,7 @@ SYM_CODE_END(virtual_mapped) /* Do the copies */ SYM_CODE_START_LOCAL_NOALIGN(swap_pages) + UNWIND_HINT_EMPTY movq %rdi, %rcx /* Put the page_list in %rcx */ xorl %edi, %edi xorl %esi, %esi -- cgit v1.2.3 From 65fb11a7f6aeae678043738d06248a4e21f4e4e4 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 10 Mar 2020 18:39:45 +0100 Subject: objtool: Optimize find_symbol_by_index() The symbol index is object wide, not per section, so it makes no sense to have the symbol_hash be part of the section object. By moving it to the elf object we avoid the linear sections iteration. This reduces the runtime of objtool on vmlinux.o from over 3 hours (I gave up) to a few minutes. The defconfig vmlinux.o has around 20k sections. Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.261852348@infradead.org --- tools/objtool/elf.c | 13 +++++-------- tools/objtool/elf.h | 3 +-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index cc4601c879ce..b188b3e075be 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -46,13 +46,11 @@ static struct section *find_section_by_index(struct elf *elf, static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) { - struct section *sec; struct symbol *sym; - list_for_each_entry(sec, &elf->sections, list) - hash_for_each_possible(sec->symbol_hash, sym, hash, idx) - if (sym->idx == idx) - return sym; + hash_for_each_possible(elf->symbol_hash, sym, hash, idx) + if (sym->idx == idx) + return sym; return NULL; } @@ -166,7 +164,6 @@ static int read_sections(struct elf *elf) INIT_LIST_HEAD(&sec->symbol_list); INIT_LIST_HEAD(&sec->rela_list); hash_init(sec->rela_hash); - hash_init(sec->symbol_hash); list_add_tail(&sec->list, &elf->sections); @@ -299,7 +296,7 @@ static int read_symbols(struct elf *elf) } sym->alias = alias; list_add(&sym->list, entry); - hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx); + hash_add(elf->symbol_hash, &sym->hash, sym->idx); } /* Create parent/child links for any cold subfunctions */ @@ -425,6 +422,7 @@ struct elf *elf_read(const char *name, int flags) } memset(elf, 0, sizeof(*elf)); + hash_init(elf->symbol_hash); INIT_LIST_HEAD(&elf->sections); elf->fd = open(name, flags); @@ -486,7 +484,6 @@ struct section *elf_create_section(struct elf *elf, const char *name, INIT_LIST_HEAD(&sec->symbol_list); INIT_LIST_HEAD(&sec->rela_list); hash_init(sec->rela_hash); - hash_init(sec->symbol_hash); list_add_tail(&sec->list, &elf->sections); diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h index a1963259b930..12229801b13f 100644 --- a/tools/objtool/elf.h +++ b/tools/objtool/elf.h @@ -27,7 +27,6 @@ struct section { struct list_head list; GElf_Shdr sh; struct list_head symbol_list; - DECLARE_HASHTABLE(symbol_hash, 8); struct list_head rela_list; DECLARE_HASHTABLE(rela_hash, 16); struct section *base, *rela; @@ -71,7 +70,7 @@ struct elf { int fd; char *name; struct list_head sections; - DECLARE_HASHTABLE(rela_hash, 16); + DECLARE_HASHTABLE(symbol_hash, 20); }; -- cgit v1.2.3 From 1e11f3fdc326d7466e43185ea943b6156143387c Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 12 Mar 2020 09:26:29 +0100 Subject: objtool: Add a statistics mode Have it print a few numbers which can be used to size the hashtables. Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.321381240@infradead.org --- tools/objtool/builtin-check.c | 3 ++- tools/objtool/builtin.h | 2 +- tools/objtool/check.c | 5 +++++ tools/objtool/elf.c | 18 +++++++++++++++++- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index c807984a03c1..10fbe75ab43d 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -17,7 +17,7 @@ #include "builtin.h" #include "check.h" -bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess; +bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats; static const char * const check_usage[] = { "objtool check [] file.o", @@ -31,6 +31,7 @@ const struct option check_options[] = { OPT_BOOLEAN('m', "module", &module, "Indicates the object will be part of a kernel module"), OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"), OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"), + OPT_BOOLEAN('s', "stats", &stats, "print statistics"), OPT_END(), }; diff --git a/tools/objtool/builtin.h b/tools/objtool/builtin.h index a32736f8d2a4..0b907902ee79 100644 --- a/tools/objtool/builtin.h +++ b/tools/objtool/builtin.h @@ -8,7 +8,7 @@ #include extern const struct option check_options[]; -extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess; +extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats; extern int cmd_check(int argc, const char **argv); extern int cmd_orc(int argc, const char **argv); diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 43f7d3c2e8b2..6df1bae2f961 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -239,6 +239,7 @@ static int decode_instructions(struct objtool_file *file) struct symbol *func; unsigned long offset; struct instruction *insn; + unsigned long nr_insns = 0; int ret; for_each_sec(file, sec) { @@ -274,6 +275,7 @@ static int decode_instructions(struct objtool_file *file) hash_add(file->insn_hash, &insn->hash, insn->offset); list_add_tail(&insn->list, &file->insn_list); + nr_insns++; } list_for_each_entry(func, &sec->symbol_list, list) { @@ -291,6 +293,9 @@ static int decode_instructions(struct objtool_file *file) } } + if (stats) + printf("nr_insns: %lu\n", nr_insns); + return 0; err: diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index b188b3e075be..ff293064b469 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -15,6 +15,7 @@ #include #include #include +#include "builtin.h" #include "elf.h" #include "warn.h" @@ -202,6 +203,9 @@ static int read_sections(struct elf *elf) sec->len = sec->sh.sh_size; } + if (stats) + printf("nr_sections: %lu\n", (unsigned long)sections_nr); + /* sanity check, one more call to elf_nextscn() should return NULL */ if (elf_nextscn(elf->elf, s)) { WARN("section entry mismatch"); @@ -299,6 +303,9 @@ static int read_symbols(struct elf *elf) hash_add(elf->symbol_hash, &sym->hash, sym->idx); } + if (stats) + printf("nr_symbols: %lu\n", (unsigned long)symbols_nr); + /* Create parent/child links for any cold subfunctions */ list_for_each_entry(sec, &elf->sections, list) { list_for_each_entry(sym, &sec->symbol_list, list) { @@ -360,6 +367,7 @@ static int read_relas(struct elf *elf) struct rela *rela; int i; unsigned int symndx; + unsigned long nr_rela, max_rela = 0, tot_rela = 0; list_for_each_entry(sec, &elf->sections, list) { if (sec->sh.sh_type != SHT_RELA) @@ -374,6 +382,7 @@ static int read_relas(struct elf *elf) sec->base->rela = sec; + nr_rela = 0; for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) { rela = malloc(sizeof(*rela)); if (!rela) { @@ -401,8 +410,15 @@ static int read_relas(struct elf *elf) list_add_tail(&rela->list, &sec->rela_list); hash_add(sec->rela_hash, &rela->hash, rela->offset); - + nr_rela++; } + max_rela = max(max_rela, nr_rela); + tot_rela += nr_rela; + } + + if (stats) { + printf("max_rela: %lu\n", max_rela); + printf("tot_rela: %lu\n", tot_rela); } return 0; -- cgit v1.2.3 From 530389968739883a61192767e1c215653ba4ba2b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 10 Mar 2020 18:43:35 +0100 Subject: objtool: Optimize find_section_by_index() In order to avoid a linear search (over 20k entries), add an section_hash to the elf object. This reduces objtool on vmlinux.o from a few minutes to around 45 seconds. Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.381249993@infradead.org --- tools/objtool/elf.c | 13 ++++++++----- tools/objtool/elf.h | 2 ++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index ff293064b469..900771365ae3 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -38,7 +38,7 @@ static struct section *find_section_by_index(struct elf *elf, { struct section *sec; - list_for_each_entry(sec, &elf->sections, list) + hash_for_each_possible(elf->section_hash, sec, hash, idx) if (sec->idx == idx) return sec; @@ -166,8 +166,6 @@ static int read_sections(struct elf *elf) INIT_LIST_HEAD(&sec->rela_list); hash_init(sec->rela_hash); - list_add_tail(&sec->list, &elf->sections); - s = elf_getscn(elf->elf, i); if (!s) { WARN_ELF("elf_getscn"); @@ -201,6 +199,9 @@ static int read_sections(struct elf *elf) } } sec->len = sec->sh.sh_size; + + list_add_tail(&sec->list, &elf->sections); + hash_add(elf->section_hash, &sec->hash, sec->idx); } if (stats) @@ -439,6 +440,7 @@ struct elf *elf_read(const char *name, int flags) memset(elf, 0, sizeof(*elf)); hash_init(elf->symbol_hash); + hash_init(elf->section_hash); INIT_LIST_HEAD(&elf->sections); elf->fd = open(name, flags); @@ -501,8 +503,6 @@ struct section *elf_create_section(struct elf *elf, const char *name, INIT_LIST_HEAD(&sec->rela_list); hash_init(sec->rela_hash); - list_add_tail(&sec->list, &elf->sections); - s = elf_newscn(elf->elf); if (!s) { WARN_ELF("elf_newscn"); @@ -579,6 +579,9 @@ struct section *elf_create_section(struct elf *elf, const char *name, shstrtab->len += strlen(name) + 1; shstrtab->changed = true; + list_add_tail(&sec->list, &elf->sections); + hash_add(elf->section_hash, &sec->hash, sec->idx); + return sec; } diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h index 12229801b13f..8c272eb515c8 100644 --- a/tools/objtool/elf.h +++ b/tools/objtool/elf.h @@ -25,6 +25,7 @@ struct section { struct list_head list; + struct hlist_node hash; GElf_Shdr sh; struct list_head symbol_list; struct list_head rela_list; @@ -71,6 +72,7 @@ struct elf { char *name; struct list_head sections; DECLARE_HASHTABLE(symbol_hash, 20); + DECLARE_HASHTABLE(section_hash, 16); }; -- cgit v1.2.3 From ae358196fac3a0b4d2a7d47a4f401e3421027b03 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 12 Mar 2020 09:32:10 +0100 Subject: objtool: Optimize find_section_by_name() In order to avoid yet another linear search of (20k) sections, add a name based hash. This reduces objtool runtime on vmlinux.o by some 10s to around 35s. Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.440174280@infradead.org --- tools/objtool/elf.c | 10 +++++++++- tools/objtool/elf.h | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 900771365ae3..20fe40d5101d 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -22,11 +22,16 @@ #define MAX_NAME_LEN 128 +static inline u32 str_hash(const char *str) +{ + return jhash(str, strlen(str), 0); +} + struct section *find_section_by_name(struct elf *elf, const char *name) { struct section *sec; - list_for_each_entry(sec, &elf->sections, list) + hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name)) if (!strcmp(sec->name, name)) return sec; @@ -202,6 +207,7 @@ static int read_sections(struct elf *elf) list_add_tail(&sec->list, &elf->sections); hash_add(elf->section_hash, &sec->hash, sec->idx); + hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name)); } if (stats) @@ -441,6 +447,7 @@ struct elf *elf_read(const char *name, int flags) hash_init(elf->symbol_hash); hash_init(elf->section_hash); + hash_init(elf->section_name_hash); INIT_LIST_HEAD(&elf->sections); elf->fd = open(name, flags); @@ -581,6 +588,7 @@ struct section *elf_create_section(struct elf *elf, const char *name, list_add_tail(&sec->list, &elf->sections); hash_add(elf->section_hash, &sec->hash, sec->idx); + hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name)); return sec; } diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h index 8c272eb515c8..ac7c46f7d9ab 100644 --- a/tools/objtool/elf.h +++ b/tools/objtool/elf.h @@ -10,6 +10,7 @@ #include #include #include +#include #ifdef LIBELF_USE_DEPRECATED # define elf_getshdrnum elf_getshnum @@ -26,6 +27,7 @@ struct section { struct list_head list; struct hlist_node hash; + struct hlist_node name_hash; GElf_Shdr sh; struct list_head symbol_list; struct list_head rela_list; @@ -73,6 +75,7 @@ struct elf { struct list_head sections; DECLARE_HASHTABLE(symbol_hash, 20); DECLARE_HASHTABLE(section_hash, 16); + DECLARE_HASHTABLE(section_name_hash, 16); }; -- cgit v1.2.3 From 2a362ecc3ec9632aeea4b9a9062db91b2bd9975a Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 12 Mar 2020 09:34:42 +0100 Subject: objtool: Optimize find_symbol_*() and read_symbols() All of: read_symbols(), find_symbol_by_offset(), find_symbol_containing(), find_containing_func() do a linear search of the symbols. Add an RB tree to make it go faster. This about halves objtool runtime on vmlinux.o, from 34s to 18s. Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.499016559@infradead.org --- tools/objtool/Build | 5 ++ tools/objtool/elf.c | 194 ++++++++++++++++++++++++++++++++++++---------------- tools/objtool/elf.h | 3 + 3 files changed, 144 insertions(+), 58 deletions(-) diff --git a/tools/objtool/Build b/tools/objtool/Build index 8dc4f0848362..66f44f5cd2a6 100644 --- a/tools/objtool/Build +++ b/tools/objtool/Build @@ -11,6 +11,7 @@ objtool-y += objtool.o objtool-y += libstring.o objtool-y += libctype.o objtool-y += str_error_r.o +objtool-y += librbtree.o CFLAGS += -I$(srctree)/tools/lib @@ -25,3 +26,7 @@ $(OUTPUT)libctype.o: ../lib/ctype.c FORCE $(OUTPUT)str_error_r.o: ../lib/str_error_r.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) + +$(OUTPUT)librbtree.o: ../lib/rbtree.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_o_c) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 20fe40d5101d..3a8b42672655 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -27,6 +27,90 @@ static inline u32 str_hash(const char *str) return jhash(str, strlen(str), 0); } +static void rb_add(struct rb_root *tree, struct rb_node *node, + int (*cmp)(struct rb_node *, const struct rb_node *)) +{ + struct rb_node **link = &tree->rb_node; + struct rb_node *parent = NULL; + + while (*link) { + parent = *link; + if (cmp(node, parent) < 0) + link = &parent->rb_left; + else + link = &parent->rb_right; + } + + rb_link_node(node, parent, link); + rb_insert_color(node, tree); +} + +static struct rb_node *rb_find_first(struct rb_root *tree, const void *key, + int (*cmp)(const void *key, const struct rb_node *)) +{ + struct rb_node *node = tree->rb_node; + struct rb_node *match = NULL; + + while (node) { + int c = cmp(key, node); + if (c <= 0) { + if (!c) + match = node; + node = node->rb_left; + } else if (c > 0) { + node = node->rb_right; + } + } + + return match; +} + +static struct rb_node *rb_next_match(struct rb_node *node, const void *key, + int (*cmp)(const void *key, const struct rb_node *)) +{ + node = rb_next(node); + if (node && cmp(key, node)) + node = NULL; + return node; +} + +#define rb_for_each(tree, node, key, cmp) \ + for ((node) = rb_find_first((tree), (key), (cmp)); \ + (node); (node) = rb_next_match((node), (key), (cmp))) + +static int symbol_to_offset(struct rb_node *a, const struct rb_node *b) +{ + struct symbol *sa = rb_entry(a, struct symbol, node); + struct symbol *sb = rb_entry(b, struct symbol, node); + + if (sa->offset < sb->offset) + return -1; + if (sa->offset > sb->offset) + return 1; + + if (sa->len < sb->len) + return -1; + if (sa->len > sb->len) + return 1; + + sa->alias = sb; + + return 0; +} + +static int symbol_by_offset(const void *key, const struct rb_node *node) +{ + const struct symbol *s = rb_entry(node, struct symbol, node); + const unsigned long *o = key; + + if (*o < s->offset) + return -1; + if (*o > s->offset + s->len) + return 1; + + return 0; +} + struct section *find_section_by_name(struct elf *elf, const char *name) { struct section *sec; @@ -63,47 +147,69 @@ static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) { - struct symbol *sym; + struct rb_node *node; - list_for_each_entry(sym, &sec->symbol_list, list) - if (sym->type != STT_SECTION && sym->offset == offset) - return sym; + rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { + struct symbol *s = rb_entry(node, struct symbol, node); + + if (s->offset == offset && s->type != STT_SECTION) + return s; + } return NULL; } struct symbol *find_func_by_offset(struct section *sec, unsigned long offset) { - struct symbol *sym; + struct rb_node *node; - list_for_each_entry(sym, &sec->symbol_list, list) - if (sym->type == STT_FUNC && sym->offset == offset) - return sym; + rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { + struct symbol *s = rb_entry(node, struct symbol, node); + + if (s->offset == offset && s->type == STT_FUNC) + return s; + } return NULL; } -struct symbol *find_symbol_by_name(struct elf *elf, const char *name) +struct symbol *find_symbol_containing(struct section *sec, unsigned long offset) { - struct section *sec; - struct symbol *sym; + struct rb_node *node; - list_for_each_entry(sec, &elf->sections, list) - list_for_each_entry(sym, &sec->symbol_list, list) - if (!strcmp(sym->name, name)) - return sym; + rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { + struct symbol *s = rb_entry(node, struct symbol, node); + + if (s->type != STT_SECTION) + return s; + } return NULL; } -struct symbol *find_symbol_containing(struct section *sec, unsigned long offset) +struct symbol *find_containing_func(struct section *sec, unsigned long offset) +{ + struct rb_node *node; + + rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) { + struct symbol *s = rb_entry(node, struct symbol, node); + + if (s->type == STT_FUNC) + return s; + } + + return NULL; +} + +struct symbol *find_symbol_by_name(struct elf *elf, const char *name) { + struct section *sec; struct symbol *sym; - list_for_each_entry(sym, &sec->symbol_list, list) - if (sym->type != STT_SECTION && - offset >= sym->offset && offset < sym->offset + sym->len) - return sym; + list_for_each_entry(sec, &elf->sections, list) + list_for_each_entry(sym, &sec->symbol_list, list) + if (!strcmp(sym->name, name)) + return sym; return NULL; } @@ -130,18 +236,6 @@ struct rela *find_rela_by_dest(struct section *sec, unsigned long offset) return find_rela_by_dest_range(sec, offset, 1); } -struct symbol *find_containing_func(struct section *sec, unsigned long offset) -{ - struct symbol *func; - - list_for_each_entry(func, &sec->symbol_list, list) - if (func->type == STT_FUNC && offset >= func->offset && - offset < func->offset + func->len) - return func; - - return NULL; -} - static int read_sections(struct elf *elf) { Elf_Scn *s = NULL; @@ -225,8 +319,9 @@ static int read_sections(struct elf *elf) static int read_symbols(struct elf *elf) { struct section *symtab, *sec; - struct symbol *sym, *pfunc, *alias; - struct list_head *entry, *tmp; + struct symbol *sym, *pfunc; + struct list_head *entry; + struct rb_node *pnode; int symbols_nr, i; char *coldstr; @@ -245,7 +340,7 @@ static int read_symbols(struct elf *elf) return -1; } memset(sym, 0, sizeof(*sym)); - alias = sym; + sym->alias = sym; sym->idx = i; @@ -283,29 +378,12 @@ static int read_symbols(struct elf *elf) sym->offset = sym->sym.st_value; sym->len = sym->sym.st_size; - /* sorted insert into a per-section list */ - entry = &sym->sec->symbol_list; - list_for_each_prev(tmp, &sym->sec->symbol_list) { - struct symbol *s; - - s = list_entry(tmp, struct symbol, list); - - if (sym->offset > s->offset) { - entry = tmp; - break; - } - - if (sym->offset == s->offset) { - if (sym->len && sym->len == s->len && alias == sym) - alias = s; - - if (sym->len >= s->len) { - entry = tmp; - break; - } - } - } - sym->alias = alias; + rb_add(&sym->sec->symbol_tree, &sym->node, symbol_to_offset); + pnode = rb_prev(&sym->node); + if (pnode) + entry = &rb_entry(pnode, struct symbol, node)->list; + else + entry = &sym->sec->symbol_list; list_add(&sym->list, entry); hash_add(elf->symbol_hash, &sym->hash, sym->idx); } diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h index ac7c46f7d9ab..e4a8d68f2ef7 100644 --- a/tools/objtool/elf.h +++ b/tools/objtool/elf.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #ifdef LIBELF_USE_DEPRECATED @@ -29,6 +30,7 @@ struct section { struct hlist_node hash; struct hlist_node name_hash; GElf_Shdr sh; + struct rb_root symbol_tree; struct list_head symbol_list; struct list_head rela_list; DECLARE_HASHTABLE(rela_hash, 16); @@ -43,6 +45,7 @@ struct section { struct symbol { struct list_head list; + struct rb_node node; struct hlist_node hash; GElf_Sym sym; struct section *sec; -- cgit v1.2.3 From 53d20720bbc8718ef86fdfe53dec0accfb593ef8 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 16 Mar 2020 10:36:53 +0100 Subject: objtool: Rename find_containing_func() For consistency; we have: find_symbol_by_offset() / find_symbol_containing() find_func_by_offset() / find_containing_func() fix that. Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.558470724@infradead.org --- tools/objtool/elf.c | 2 +- tools/objtool/elf.h | 2 +- tools/objtool/warn.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 3a8b42672655..07db4df18588 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -187,7 +187,7 @@ struct symbol *find_symbol_containing(struct section *sec, unsigned long offset) return NULL; } -struct symbol *find_containing_func(struct section *sec, unsigned long offset) +struct symbol *find_func_containing(struct section *sec, unsigned long offset) { struct rb_node *node; diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h index e4a8d68f2ef7..d18f466829b0 100644 --- a/tools/objtool/elf.h +++ b/tools/objtool/elf.h @@ -91,7 +91,7 @@ struct symbol *find_symbol_containing(struct section *sec, unsigned long offset) struct rela *find_rela_by_dest(struct section *sec, unsigned long offset); struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, unsigned int len); -struct symbol *find_containing_func(struct section *sec, unsigned long offset); +struct symbol *find_func_containing(struct section *sec, unsigned long offset); struct section *elf_create_section(struct elf *elf, const char *name, size_t entsize, int nr); struct section *elf_create_rela_section(struct elf *elf, struct section *base); diff --git a/tools/objtool/warn.h b/tools/objtool/warn.h index cbb0a02b7480..7799f60de80a 100644 --- a/tools/objtool/warn.h +++ b/tools/objtool/warn.h @@ -21,7 +21,7 @@ static inline char *offstr(struct section *sec, unsigned long offset) char *name, *str; unsigned long name_off; - func = find_containing_func(sec, offset); + func = find_func_containing(sec, offset); if (func) { name = func->name; name_off = offset - func->offset; -- cgit v1.2.3 From 513b5ca6b5fbeb766999fb1a4bc9a55c9fb7c9ca Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 12 Mar 2020 10:20:46 +0100 Subject: objtool: Resize insn_hash Perf shows we're spending a lot of time in find_insn() and the statistics show we have around 3.2 million instruction. Increase the hash table size to reduce the bucket load from around 50 to 3. This shaves about 2s off of objtool on vmlinux.o runtime, down to 16s. Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.617882545@infradead.org --- tools/objtool/check.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/objtool/check.h b/tools/objtool/check.h index 6d875ca6fce0..f0ce8ffe7135 100644 --- a/tools/objtool/check.h +++ b/tools/objtool/check.h @@ -50,7 +50,7 @@ struct instruction { struct objtool_file { struct elf *elf; struct list_head insn_list; - DECLARE_HASHTABLE(insn_hash, 16); + DECLARE_HASHTABLE(insn_hash, 20); bool ignore_unreachables, c_file, hints, rodata; }; -- cgit v1.2.3 From cdb3d057a17d56363a831e486ea39e4c389a6cf9 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 12 Mar 2020 10:17:38 +0100 Subject: objtool: Optimize find_symbol_by_name() Perf showed that find_symbol_by_name() takes time; add a symbol name hash. This shaves another second off of objtool on vmlinux.o runtime, down to 15 seconds. Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.676865656@infradead.org --- tools/objtool/elf.c | 10 +++++----- tools/objtool/elf.h | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 07db4df18588..43abae763b3b 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -203,13 +203,11 @@ struct symbol *find_func_containing(struct section *sec, unsigned long offset) struct symbol *find_symbol_by_name(struct elf *elf, const char *name) { - struct section *sec; struct symbol *sym; - list_for_each_entry(sec, &elf->sections, list) - list_for_each_entry(sym, &sec->symbol_list, list) - if (!strcmp(sym->name, name)) - return sym; + hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name)) + if (!strcmp(sym->name, name)) + return sym; return NULL; } @@ -386,6 +384,7 @@ static int read_symbols(struct elf *elf) entry = &sym->sec->symbol_list; list_add(&sym->list, entry); hash_add(elf->symbol_hash, &sym->hash, sym->idx); + hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name)); } if (stats) @@ -524,6 +523,7 @@ struct elf *elf_read(const char *name, int flags) memset(elf, 0, sizeof(*elf)); hash_init(elf->symbol_hash); + hash_init(elf->symbol_name_hash); hash_init(elf->section_hash); hash_init(elf->section_name_hash); INIT_LIST_HEAD(&elf->sections); diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h index d18f466829b0..3088d925d640 100644 --- a/tools/objtool/elf.h +++ b/tools/objtool/elf.h @@ -47,6 +47,7 @@ struct symbol { struct list_head list; struct rb_node node; struct hlist_node hash; + struct hlist_node name_hash; GElf_Sym sym; struct section *sec; char *name; @@ -77,6 +78,7 @@ struct elf { char *name; struct list_head sections; DECLARE_HASHTABLE(symbol_hash, 20); + DECLARE_HASHTABLE(symbol_name_hash, 20); DECLARE_HASHTABLE(section_hash, 16); DECLARE_HASHTABLE(section_name_hash, 16); }; -- cgit v1.2.3 From 8b5fa6bc326bf02f293b5a39a8f5b3de816265d3 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 12 Mar 2020 11:23:36 +0100 Subject: objtool: Optimize read_sections() Perf showed that __hash_init() is a significant portion of read_sections(), so instead of doing a per section rela_hash, use an elf-wide rela_hash. Statistics show us there are about 1.1 million relas, so size it accordingly. This reduces the objtool on vmlinux.o runtime to a third, from 15 to 5 seconds. Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.739153726@infradead.org --- tools/objtool/check.c | 18 +++++++++--------- tools/objtool/elf.c | 24 ++++++++++++++---------- tools/objtool/elf.h | 21 +++++++++++++++++---- tools/objtool/orc_gen.c | 9 +++++---- tools/objtool/special.c | 4 ++-- 5 files changed, 47 insertions(+), 29 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 6df1bae2f961..54a604381a03 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -569,8 +569,8 @@ static int add_jump_destinations(struct objtool_file *file) if (insn->ignore || insn->offset == FAKE_JUMP_OFFSET) continue; - rela = find_rela_by_dest_range(insn->sec, insn->offset, - insn->len); + rela = find_rela_by_dest_range(file->elf, insn->sec, + insn->offset, insn->len); if (!rela) { dest_sec = insn->sec; dest_off = insn->offset + insn->len + insn->immediate; @@ -666,8 +666,8 @@ static int add_call_destinations(struct objtool_file *file) if (insn->type != INSN_CALL) continue; - rela = find_rela_by_dest_range(insn->sec, insn->offset, - insn->len); + rela = find_rela_by_dest_range(file->elf, insn->sec, + insn->offset, insn->len); if (!rela) { dest_off = insn->offset + insn->len + insn->immediate; insn->call_dest = find_func_by_offset(insn->sec, dest_off); @@ -796,7 +796,7 @@ static int handle_group_alt(struct objtool_file *file, */ if ((insn->offset != special_alt->new_off || (insn->type != INSN_CALL && !is_static_jump(insn))) && - find_rela_by_dest_range(insn->sec, insn->offset, insn->len)) { + find_rela_by_dest_range(file->elf, insn->sec, insn->offset, insn->len)) { WARN_FUNC("unsupported relocation in alternatives section", insn->sec, insn->offset); @@ -1066,8 +1066,8 @@ static struct rela *find_jump_table(struct objtool_file *file, break; /* look for a relocation which references .rodata */ - text_rela = find_rela_by_dest_range(insn->sec, insn->offset, - insn->len); + text_rela = find_rela_by_dest_range(file->elf, insn->sec, + insn->offset, insn->len); if (!text_rela || text_rela->sym->type != STT_SECTION || !text_rela->sym->sec->rodata) continue; @@ -1096,7 +1096,7 @@ static struct rela *find_jump_table(struct objtool_file *file, * should reference text in the same function as the original * instruction. */ - table_rela = find_rela_by_dest(table_sec, table_offset); + table_rela = find_rela_by_dest(file->elf, table_sec, table_offset); if (!table_rela) continue; dest_insn = find_insn(file, table_rela->sym->sec, table_rela->addend); @@ -1232,7 +1232,7 @@ static int read_unwind_hints(struct objtool_file *file) for (i = 0; i < sec->len / sizeof(struct unwind_hint); i++) { hint = (struct unwind_hint *)sec->data->d_buf + i; - rela = find_rela_by_dest(sec, i * sizeof(*hint)); + rela = find_rela_by_dest(file->elf, sec, i * sizeof(*hint)); if (!rela) { WARN("can't find rela for unwind_hints[%d]", i); return -1; diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 43abae763b3b..8a0a1bc18cd7 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -212,8 +212,8 @@ struct symbol *find_symbol_by_name(struct elf *elf, const char *name) return NULL; } -struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, - unsigned int len) +struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec, + unsigned long offset, unsigned int len) { struct rela *rela; unsigned long o; @@ -221,17 +221,22 @@ struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, if (!sec->rela) return NULL; - for (o = offset; o < offset + len; o++) - hash_for_each_possible(sec->rela->rela_hash, rela, hash, o) - if (rela->offset == o) + sec = sec->rela; + + for (o = offset; o < offset + len; o++) { + hash_for_each_possible(elf->rela_hash, rela, hash, + sec_offset_hash(sec, o)) { + if (rela->sec == sec && rela->offset == o) return rela; + } + } return NULL; } -struct rela *find_rela_by_dest(struct section *sec, unsigned long offset) +struct rela *find_rela_by_dest(struct elf *elf, struct section *sec, unsigned long offset) { - return find_rela_by_dest_range(sec, offset, 1); + return find_rela_by_dest_range(elf, sec, offset, 1); } static int read_sections(struct elf *elf) @@ -261,7 +266,6 @@ static int read_sections(struct elf *elf) INIT_LIST_HEAD(&sec->symbol_list); INIT_LIST_HEAD(&sec->rela_list); - hash_init(sec->rela_hash); s = elf_getscn(elf->elf, i); if (!s) { @@ -493,7 +497,7 @@ static int read_relas(struct elf *elf) } list_add_tail(&rela->list, &sec->rela_list); - hash_add(sec->rela_hash, &rela->hash, rela->offset); + hash_add(elf->rela_hash, &rela->hash, rela_hash(rela)); nr_rela++; } max_rela = max(max_rela, nr_rela); @@ -526,6 +530,7 @@ struct elf *elf_read(const char *name, int flags) hash_init(elf->symbol_name_hash); hash_init(elf->section_hash); hash_init(elf->section_name_hash); + hash_init(elf->rela_hash); INIT_LIST_HEAD(&elf->sections); elf->fd = open(name, flags); @@ -586,7 +591,6 @@ struct section *elf_create_section(struct elf *elf, const char *name, INIT_LIST_HEAD(&sec->symbol_list); INIT_LIST_HEAD(&sec->rela_list); - hash_init(sec->rela_hash); s = elf_newscn(elf->elf); if (!s) { diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h index 3088d925d640..dfd2431ef693 100644 --- a/tools/objtool/elf.h +++ b/tools/objtool/elf.h @@ -33,7 +33,6 @@ struct section { struct rb_root symbol_tree; struct list_head symbol_list; struct list_head rela_list; - DECLARE_HASHTABLE(rela_hash, 16); struct section *base, *rela; struct symbol *sym; Elf_Data *data; @@ -81,8 +80,22 @@ struct elf { DECLARE_HASHTABLE(symbol_name_hash, 20); DECLARE_HASHTABLE(section_hash, 16); DECLARE_HASHTABLE(section_name_hash, 16); + DECLARE_HASHTABLE(rela_hash, 20); }; +static inline u32 sec_offset_hash(struct section *sec, unsigned long offset) +{ + u32 ol = offset, oh = offset >> 32, idx = sec->idx; + + __jhash_mix(ol, oh, idx); + + return ol; +} + +static inline u32 rela_hash(struct rela *rela) +{ + return sec_offset_hash(rela->sec, rela->offset); +} struct elf *elf_read(const char *name, int flags); struct section *find_section_by_name(struct elf *elf, const char *name); @@ -90,9 +103,9 @@ 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_name(struct elf *elf, const char *name); struct symbol *find_symbol_containing(struct section *sec, unsigned long offset); -struct rela *find_rela_by_dest(struct section *sec, unsigned long offset); -struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, - unsigned int len); +struct rela *find_rela_by_dest(struct elf *elf, struct section *sec, unsigned long offset); +struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec, + unsigned long offset, unsigned int len); struct symbol *find_func_containing(struct section *sec, unsigned long offset); struct section *elf_create_section(struct elf *elf, const char *name, size_t entsize, int nr); diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c index 27a4112848c2..41e4a2754da4 100644 --- a/tools/objtool/orc_gen.c +++ b/tools/objtool/orc_gen.c @@ -81,7 +81,7 @@ int create_orc(struct objtool_file *file) return 0; } -static int create_orc_entry(struct section *u_sec, struct section *ip_relasec, +static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relasec, unsigned int idx, struct section *insn_sec, unsigned long insn_off, struct orc_entry *o) { @@ -109,9 +109,10 @@ static int create_orc_entry(struct section *u_sec, struct section *ip_relasec, rela->addend = insn_off; rela->type = R_X86_64_PC32; rela->offset = idx * sizeof(int); + rela->sec = ip_relasec; list_add_tail(&rela->list, &ip_relasec->rela_list); - hash_add(ip_relasec->rela_hash, &rela->hash, rela->offset); + hash_add(elf->rela_hash, &rela->hash, rela_hash(rela)); return 0; } @@ -182,7 +183,7 @@ int create_orc_sections(struct objtool_file *file) if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc, sizeof(struct orc_entry))) { - if (create_orc_entry(u_sec, ip_relasec, idx, + if (create_orc_entry(file->elf, u_sec, ip_relasec, idx, insn->sec, insn->offset, &insn->orc)) return -1; @@ -194,7 +195,7 @@ int create_orc_sections(struct objtool_file *file) /* section terminator */ if (prev_insn) { - if (create_orc_entry(u_sec, ip_relasec, idx, + if (create_orc_entry(file->elf, u_sec, ip_relasec, idx, prev_insn->sec, prev_insn->offset + prev_insn->len, &empty)) diff --git a/tools/objtool/special.c b/tools/objtool/special.c index fdbaa611146d..e74e0189de22 100644 --- a/tools/objtool/special.c +++ b/tools/objtool/special.c @@ -118,7 +118,7 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry, } } - orig_rela = find_rela_by_dest(sec, offset + entry->orig); + orig_rela = find_rela_by_dest(elf, sec, offset + entry->orig); if (!orig_rela) { WARN_FUNC("can't find orig rela", sec, offset + entry->orig); return -1; @@ -133,7 +133,7 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry, alt->orig_off = orig_rela->addend; if (!entry->group || alt->new_len) { - new_rela = find_rela_by_dest(sec, offset + entry->new); + new_rela = find_rela_by_dest(elf, sec, offset + entry->new); if (!new_rela) { WARN_FUNC("can't find new rela", sec, offset + entry->new); -- cgit v1.2.3 From 8887a86eddd93ca396ca35f7b41fb14ed412f85d Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 11 Mar 2020 23:07:42 +0100 Subject: objtool: Delete cleanup() Perf shows we spend a measurable amount of time spend cleaning up right before we exit anyway. Avoid the needsless work and just terminate. This reduces objtool on vmlinux.o runtime from 5.4s to 4.8s Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.800720170@infradead.org --- tools/objtool/check.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 54a604381a03..0c9c9ad47467 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -2458,23 +2458,6 @@ static int validate_reachable_instructions(struct objtool_file *file) return 0; } -static void cleanup(struct objtool_file *file) -{ - struct instruction *insn, *tmpinsn; - struct alternative *alt, *tmpalt; - - list_for_each_entry_safe(insn, tmpinsn, &file->insn_list, list) { - list_for_each_entry_safe(alt, tmpalt, &insn->alts, list) { - list_del(&alt->list); - free(alt); - } - list_del(&insn->list); - hash_del(&insn->hash); - free(insn); - } - elf_close(file->elf); -} - static struct objtool_file file; int check(const char *_objname, bool orc) @@ -2542,8 +2525,6 @@ int check(const char *_objname, bool orc) } out: - cleanup(&file); - if (ret < 0) { /* * Fatal error. The binary is corrupt or otherwise broken in -- cgit v1.2.3 From 74b873e49d92f90deb41d1a2a8fbb70328aebd67 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 12 Mar 2020 11:30:50 +0100 Subject: objtool: Optimize find_rela_by_dest_range() Perf shows there is significant time in find_rela_by_dest(); this is because we have to iterate the address space per byte, looking for relocation entries. Optimize this by reducing the address space granularity. This reduces objtool on vmlinux.o runtime from 4.8 to 4.4 seconds. Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.861321325@infradead.org --- tools/objtool/elf.c | 15 +++++++++++---- tools/objtool/elf.h | 16 +++++++++++++++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 8a0a1bc18cd7..09ddc8f1def3 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -215,7 +215,7 @@ struct symbol *find_symbol_by_name(struct elf *elf, const char *name) struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec, unsigned long offset, unsigned int len) { - struct rela *rela; + struct rela *rela, *r = NULL; unsigned long o; if (!sec->rela) @@ -223,12 +223,19 @@ struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec, sec = sec->rela; - for (o = offset; o < offset + len; o++) { + for_offset_range(o, offset, offset + len) { hash_for_each_possible(elf->rela_hash, rela, hash, sec_offset_hash(sec, o)) { - if (rela->sec == sec && rela->offset == o) - return rela; + if (rela->sec != sec) + continue; + + if (rela->offset >= offset && rela->offset < offset + len) { + if (!r || rela->offset < r->offset) + r = rela; + } } + if (r) + return r; } return NULL; diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h index dfd2431ef693..ebbb10c61e24 100644 --- a/tools/objtool/elf.h +++ b/tools/objtool/elf.h @@ -83,9 +83,23 @@ struct elf { DECLARE_HASHTABLE(rela_hash, 20); }; +#define OFFSET_STRIDE_BITS 4 +#define OFFSET_STRIDE (1UL << OFFSET_STRIDE_BITS) +#define OFFSET_STRIDE_MASK (~(OFFSET_STRIDE - 1)) + +#define for_offset_range(_offset, _start, _end) \ + for (_offset = ((_start) & OFFSET_STRIDE_MASK); \ + _offset <= ((_end) & OFFSET_STRIDE_MASK); \ + _offset += OFFSET_STRIDE) + static inline u32 sec_offset_hash(struct section *sec, unsigned long offset) { - u32 ol = offset, oh = offset >> 32, idx = sec->idx; + u32 ol, oh, idx = sec->idx; + + offset &= OFFSET_STRIDE_MASK; + + ol = offset; + oh = offset >> 32; __jhash_mix(ol, oh, idx); -- cgit v1.2.3 From 350994bf95414d6da67a72f27d7ac3832ce3725d Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 23 Mar 2020 20:57:13 +0100 Subject: objtool: Re-arrange validate_functions() In preparation to adding a vmlinux.o specific pass, rearrange some code. No functional changes intended. Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200324160924.924304616@infradead.org --- tools/objtool/check.c | 52 +++++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 0c9c9ad47467..0bfcb390ca73 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -2395,9 +2395,8 @@ static bool ignore_unreachable_insn(struct instruction *insn) return false; } -static int validate_functions(struct objtool_file *file) +static int validate_section(struct objtool_file *file, struct section *sec) { - struct section *sec; struct symbol *func; struct instruction *insn; struct insn_state state; @@ -2410,36 +2409,45 @@ static int validate_functions(struct objtool_file *file) CFI_NUM_REGS * sizeof(struct cfi_reg)); state.stack_size = initial_func_cfi.cfa.offset; - for_each_sec(file, sec) { - list_for_each_entry(func, &sec->symbol_list, list) { - if (func->type != STT_FUNC) - continue; + list_for_each_entry(func, &sec->symbol_list, list) { + if (func->type != STT_FUNC) + continue; - if (!func->len) { - WARN("%s() is missing an ELF size annotation", - func->name); - warnings++; - } + if (!func->len) { + WARN("%s() is missing an ELF size annotation", + func->name); + warnings++; + } - if (func->pfunc != func || func->alias != func) - continue; + if (func->pfunc != func || func->alias != func) + continue; - insn = find_insn(file, sec, func->offset); - if (!insn || insn->ignore || insn->visited) - continue; + insn = find_insn(file, sec, func->offset); + if (!insn || insn->ignore || insn->visited) + continue; - state.uaccess = func->uaccess_safe; + state.uaccess = func->uaccess_safe; - ret = validate_branch(file, func, insn, state); - if (ret && backtrace) - BT_FUNC("<=== (func)", insn); - warnings += ret; - } + ret = validate_branch(file, func, insn, state); + if (ret && backtrace) + BT_FUNC("<=== (func)", insn); + warnings += ret; } return warnings; } +static int validate_functions(struct objtool_file *file) +{ + struct section *sec; + int warnings = 0; + + for_each_sec(file, sec) + warnings += validate_section(file, sec); + + return warnings; +} + static int validate_reachable_instructions(struct objtool_file *file) { struct instruction *insn; -- cgit v1.2.3