mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/
synced 2025-04-19 20:58:31 +09:00
Objtool changes for v6.15:
- The biggest change is the new option to automatically fail the build on objtool warnings: CONFIG_OBJTOOL_WERROR. While there are no currently known unfixed false positives left, such an expansion in the severity of objtool warnings inevitably creates a risk of build failures, so it's disabled by default and depends on !COMPILE_TEST, so it shouldn't be enabled on allyesconfig/allmodconfig builds and won't be forced on people who just accept build-time defaults in 'make oldconfig'. While the option is strongly recommended, only people who enable it explicitly should see it. (Josh Poimboeuf) - Disable branch profiling in noinstr code with a broad brush that includes all of arch/x86/ and kernel/sched/. (Josh Poimboeuf) - Create backup object files on objtool errors and print exact objtool arguments to make failure analysis easier (Josh Poimboeuf) - Improve noreturn handling (Josh Poimboeuf) - Improve rodata handling (Tiezhu Yang) - Support jump tables, switch tables and goto tables on LoongArch (Tiezhu Yang) - Misc cleanups and fixes (Josh Poimboeuf, David Engraf, Ingo Molnar) Signed-off-by: Ingo Molnar <mingo@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAmfefkARHG1pbmdvQGtl cm5lbC5vcmcACgkQEnMQ0APhK1inlRAAvd9Hom2qh9Iu+KYYF58vsg9zsxWZA6I1 blouKI4SUA8Xjuw6Nihx+emaPaMW1boGSLTNsFzrCa3S1+4UHTTp/Y8snZWJ/Mc/ Peg52N6u/LIcoQM+vNJYRtd9y4wabX87vl0qTxte0kB0Neps3/yQvxtUa2K1srXp 8nwHK+PdzNsgPuIrIiNc9ymsPvbqFHmVIRRVNVKX4BlPJi2kJs9kx43kszweQR/X kW/bs1315m1HS5i02K0Zs/XdOZHLsk9ERu+aviBJV1txrgZIukATIqbODiI+3RZX 0oa3KxfzEVFN2k3OukrezV2INzETkN+oOSTAZIUOqwSVe+8rdQVBSdYT6svYn/yy aS8Bi5Mm1nfizTU+cRrzU7FxWCmwsxm9r4fHPTV8Owjxg0uoGk/E/qlvERuR2rpA p2tHMo1lp2Yo+VZBZPfm5KDHFG4tSGhF9eav2bqSI7/Kf5AWxRl8kBs5iLrcxsXh 4qk3FalnuM7A+1McAUcBJAvM897Yie0s2G83ZipyYyA6U3LSBhBMWh9FlIiAjuIh YnX6IFkW9tVzVZpJFEGQn+2Ewl5Y2Go5bokKk03vkWCZCgg+hEUsVh6Cnm1ocZpO Ll3/UF4i8XjjyuAuHDn6mzyHIgch2xRN02v7dJSb09+O9b8vIoPoSqbWSLJUOBqf r6UesXDG8mY= =iswJ -----END PGP SIGNATURE----- Merge tag 'objtool-core-2025-03-22' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull objtool updates from Ingo Molnar: - The biggest change is the new option to automatically fail the build on objtool warnings: CONFIG_OBJTOOL_WERROR. While there are no currently known unfixed false positives left, such an expansion in the severity of objtool warnings inevitably creates a risk of build failures, so it's disabled by default and depends on !COMPILE_TEST, so it shouldn't be enabled on allyesconfig/allmodconfig builds and won't be forced on people who just accept build-time defaults in 'make oldconfig'. While the option is strongly recommended, only people who enable it explicitly should see it. (Josh Poimboeuf) - Disable branch profiling in noinstr code with a broad brush that includes all of arch/x86/ and kernel/sched/. (Josh Poimboeuf) - Create backup object files on objtool errors and print exact objtool arguments to make failure analysis easier (Josh Poimboeuf) - Improve noreturn handling (Josh Poimboeuf) - Improve rodata handling (Tiezhu Yang) - Support jump tables, switch tables and goto tables on LoongArch (Tiezhu Yang) - Misc cleanups and fixes (Josh Poimboeuf, David Engraf, Ingo Molnar) * tag 'objtool-core-2025-03-22' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (22 commits) tracing: Disable branch profiling in noinstr code objtool: Use O_CREAT with explicit mode mask objtool: Add CONFIG_OBJTOOL_WERROR objtool: Create backup on error and print args objtool: Change "warning:" to "error:" for --Werror objtool: Add --Werror option objtool: Add --output option objtool: Upgrade "Linked object detected" warning to error objtool: Consolidate option validation objtool: Remove --unret dependency on --rethunk objtool: Increase per-function WARN_FUNC() rate limit objtool: Update documentation objtool: Improve __noreturn annotation warning objtool: Fix error handling inconsistencies in check() x86/traps: Make exc_double_fault() consistently noreturn LoongArch: Enable jump table for objtool objtool/LoongArch: Add support for goto table objtool/LoongArch: Add support for switch table objtool: Handle PC relative relocation type objtool: Handle different entry size of rodata ...
This commit is contained in:
commit
5a658afd46
@ -291,6 +291,9 @@ config AS_HAS_LBT_EXTENSION
|
||||
config AS_HAS_LVZ_EXTENSION
|
||||
def_bool $(as-instr,hvcl 0)
|
||||
|
||||
config CC_HAS_ANNOTATE_TABLEJUMP
|
||||
def_bool $(cc-option,-mannotate-tablejump)
|
||||
|
||||
menu "Kernel type and options"
|
||||
|
||||
source "kernel/Kconfig.hz"
|
||||
|
@ -101,7 +101,11 @@ KBUILD_AFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma)
|
||||
KBUILD_CFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma)-mthin-add-sub)
|
||||
|
||||
ifdef CONFIG_OBJTOOL
|
||||
KBUILD_CFLAGS += -fno-jump-tables
|
||||
ifdef CONFIG_CC_HAS_ANNOTATE_TABLEJUMP
|
||||
KBUILD_CFLAGS += -mannotate-tablejump
|
||||
else
|
||||
KBUILD_CFLAGS += -fno-jump-tables # keep compatibility with older compilers
|
||||
endif
|
||||
endif
|
||||
|
||||
KBUILD_RUSTFLAGS += --target=loongarch64-unknown-none-softfloat -Ccode-model=small
|
||||
|
@ -1,4 +1,8 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# Branch profiling isn't noinstr-safe. Disable it for arch/x86/*
|
||||
subdir-ccflags-$(CONFIG_TRACE_BRANCH_PROFILING) += -DDISABLE_BRANCH_PROFILING
|
||||
|
||||
obj-$(CONFIG_ARCH_HAS_CC_PLATFORM) += coco/
|
||||
|
||||
obj-y += entry/
|
||||
|
@ -9,8 +9,6 @@
|
||||
|
||||
#define pr_fmt(fmt) "SEV: " fmt
|
||||
|
||||
#define DISABLE_BRANCH_PROFILING
|
||||
|
||||
#include <linux/sched/debug.h> /* For show_regs() */
|
||||
#include <linux/percpu-defs.h>
|
||||
#include <linux/cc_platform.h>
|
||||
|
@ -5,8 +5,6 @@
|
||||
* Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
|
||||
*/
|
||||
|
||||
#define DISABLE_BRANCH_PROFILING
|
||||
|
||||
/* cpu_feature_enabled() cannot be used this early */
|
||||
#define USE_EARLY_PGTABLE_L5
|
||||
|
||||
|
@ -379,6 +379,21 @@ __visible void __noreturn handle_stack_overflow(struct pt_regs *regs,
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Prevent the compiler and/or objtool from marking the !CONFIG_X86_ESPFIX64
|
||||
* version of exc_double_fault() as noreturn. Otherwise the noreturn mismatch
|
||||
* between configs triggers objtool warnings.
|
||||
*
|
||||
* This is a temporary hack until we have compiler or plugin support for
|
||||
* annotating noreturns.
|
||||
*/
|
||||
#ifdef CONFIG_X86_ESPFIX64
|
||||
#define always_true() true
|
||||
#else
|
||||
bool always_true(void);
|
||||
bool __weak always_true(void) { return true; }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Runs on an IST stack for x86_64 and on a special task stack for x86_32.
|
||||
*
|
||||
@ -514,7 +529,8 @@ DEFINE_IDTENTRY_DF(exc_double_fault)
|
||||
|
||||
pr_emerg("PANIC: double fault, error_code: 0x%lx\n", error_code);
|
||||
die("double fault", regs, error_code);
|
||||
panic("Machine halted.");
|
||||
if (always_true())
|
||||
panic("Machine halted.");
|
||||
instrumentation_end();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#define DISABLE_BRANCH_PROFILING
|
||||
#define pr_fmt(fmt) "kasan: " fmt
|
||||
|
||||
/* cpu_feature_enabled() cannot be used this early */
|
||||
|
@ -7,8 +7,6 @@
|
||||
* Author: Tom Lendacky <thomas.lendacky@amd.com>
|
||||
*/
|
||||
|
||||
#define DISABLE_BRANCH_PROFILING
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mm.h>
|
||||
|
@ -7,8 +7,6 @@
|
||||
* Author: Tom Lendacky <thomas.lendacky@amd.com>
|
||||
*/
|
||||
|
||||
#define DISABLE_BRANCH_PROFILING
|
||||
|
||||
/*
|
||||
* Since we're dealing with identity mappings, physical and virtual
|
||||
* addresses are the same, so override these defines which are ultimately
|
||||
|
@ -5,6 +5,10 @@
|
||||
|
||||
ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT
|
||||
|
||||
ifdef CONFIG_TRACE_BRANCH_PROFILING
|
||||
CFLAGS_processor_idle.o += -DDISABLE_BRANCH_PROFILING
|
||||
endif
|
||||
|
||||
#
|
||||
# ACPI Boot-Time Table Parsing
|
||||
#
|
||||
|
@ -3,6 +3,9 @@
|
||||
# Makefile for cpuidle.
|
||||
#
|
||||
|
||||
# Branch profiling isn't noinstr-safe
|
||||
ccflags-$(CONFIG_TRACE_BRANCH_PROFILING) += -DDISABLE_BRANCH_PROFILING
|
||||
|
||||
obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
|
||||
obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
|
||||
obj-$(CONFIG_DT_IDLE_STATES) += dt_idle_states.o
|
||||
|
@ -1,3 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_INTEL_IDLE) += intel_idle.o
|
||||
|
||||
# Branch profiling isn't noinstr-safe
|
||||
ccflags-$(CONFIG_TRACE_BRANCH_PROFILING) += -DDISABLE_BRANCH_PROFILING
|
||||
|
||||
obj-$(CONFIG_INTEL_IDLE) += intel_idle.o
|
||||
|
@ -21,6 +21,11 @@ ifdef CONFIG_FUNCTION_TRACER
|
||||
CFLAGS_REMOVE_irq_work.o = $(CC_FLAGS_FTRACE)
|
||||
endif
|
||||
|
||||
# Branch profiling isn't noinstr-safe
|
||||
ifdef CONFIG_TRACE_BRANCH_PROFILING
|
||||
CFLAGS_context_tracking.o += -DDISABLE_BRANCH_PROFILING
|
||||
endif
|
||||
|
||||
# Prevents flicker of uninteresting __do_softirq()/__local_bh_disable_ip()
|
||||
# in coverage traces.
|
||||
KCOV_INSTRUMENT_softirq.o := n
|
||||
|
@ -6,6 +6,9 @@ KASAN_SANITIZE := n
|
||||
UBSAN_SANITIZE := n
|
||||
KCOV_INSTRUMENT := n
|
||||
|
||||
# Branch profiling isn't noinstr-safe
|
||||
ccflags-$(CONFIG_TRACE_BRANCH_PROFILING) += -DDISABLE_BRANCH_PROFILING
|
||||
|
||||
CFLAGS_REMOVE_common.o = -fstack-protector -fstack-protector-strong
|
||||
CFLAGS_common.o += -fno-stack-protector
|
||||
|
||||
|
@ -22,6 +22,11 @@ ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y)
|
||||
CFLAGS_core.o := $(PROFILING) -fno-omit-frame-pointer
|
||||
endif
|
||||
|
||||
# Branch profiling isn't noinstr-safe
|
||||
ifdef CONFIG_TRACE_BRANCH_PROFILING
|
||||
CFLAGS_build_policy.o += -DDISABLE_BRANCH_PROFILING
|
||||
CFLAGS_build_utility.o += -DDISABLE_BRANCH_PROFILING
|
||||
endif
|
||||
#
|
||||
# Build efficiency:
|
||||
#
|
||||
|
@ -1,4 +1,10 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# Branch profiling isn't noinstr-safe
|
||||
ifdef CONFIG_TRACE_BRANCH_PROFILING
|
||||
CFLAGS_sched_clock.o += -DDISABLE_BRANCH_PROFILING
|
||||
endif
|
||||
|
||||
obj-y += time.o timer.o hrtimer.o sleep_timeout.o
|
||||
obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o
|
||||
obj-y += timeconv.o timecounter.o alarmtimer.o
|
||||
|
@ -545,6 +545,17 @@ config FRAME_POINTER
|
||||
config OBJTOOL
|
||||
bool
|
||||
|
||||
config OBJTOOL_WERROR
|
||||
bool "Upgrade objtool warnings to errors"
|
||||
depends on OBJTOOL && !COMPILE_TEST
|
||||
help
|
||||
Fail the build on objtool warnings.
|
||||
|
||||
Objtool warnings can indicate kernel instability, including boot
|
||||
failures. This option is highly recommended.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config STACK_VALIDATION
|
||||
bool "Compile-time stack metadata validation"
|
||||
depends on HAVE_STACK_VALIDATION && UNWINDER_FRAME_POINTER
|
||||
|
@ -5,6 +5,11 @@
|
||||
|
||||
ccflags-remove-$(CONFIG_FUNCTION_TRACER) += $(CC_FLAGS_FTRACE)
|
||||
|
||||
# Branch profiling isn't noinstr-safe
|
||||
ifdef CONFIG_TRACE_BRANCH_PROFILING
|
||||
CFLAGS_smp_processor_id.o += -DDISABLE_BRANCH_PROFILING
|
||||
endif
|
||||
|
||||
# These files are disabled because they produce lots of non-interesting and/or
|
||||
# flaky coverage that is not a function of syscall inputs. For example,
|
||||
# rbtree can be global and individual rotations don't correlate with inputs.
|
||||
|
@ -277,6 +277,7 @@ objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call
|
||||
objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess
|
||||
objtool-args-$(CONFIG_GCOV_KERNEL) += --no-unreachable
|
||||
objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
|
||||
objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror --backtrace
|
||||
|
||||
objtool-args = $(objtool-args-y) \
|
||||
$(if $(delay-objtool), --link) \
|
||||
|
@ -28,6 +28,15 @@ Objtool has the following features:
|
||||
sites, enabling the kernel to patch them inline, to prevent "thunk
|
||||
funneling" for both security and performance reasons
|
||||
|
||||
- Return thunk validation -- validates return thunks are used for
|
||||
certain CPU mitigations including Retbleed and SRSO
|
||||
|
||||
- Return thunk annotation -- annotates all return thunk sites so kernel
|
||||
can patch them inline, depending on enabled mitigations
|
||||
|
||||
- Return thunk training valiation -- validate that all entry paths
|
||||
untrain a "safe return" before the first return (or call)
|
||||
|
||||
- Non-instrumentation validation -- validates non-instrumentable
|
||||
("noinstr") code rules, preventing instrumentation in low-level C
|
||||
entry code
|
||||
@ -53,6 +62,9 @@ Objtool has the following features:
|
||||
- Function entry annotation -- annotates function entries, enabling
|
||||
kernel function tracing
|
||||
|
||||
- Function preamble (prefix) annotation and/or symbol generation -- used
|
||||
for FineIBT and call depth tracking
|
||||
|
||||
- Other toolchain hacks which will go unmentioned at this time...
|
||||
|
||||
Each feature can be enabled individually or in combination using the
|
||||
@ -197,19 +209,17 @@ To achieve the validation, objtool enforces the following rules:
|
||||
|
||||
1. Each callable function must be annotated as such with the ELF
|
||||
function type. In asm code, this is typically done using the
|
||||
ENTRY/ENDPROC macros. If objtool finds a return instruction
|
||||
SYM_FUNC_{START,END} macros. If objtool finds a return instruction
|
||||
outside of a function, it flags an error since that usually indicates
|
||||
callable code which should be annotated accordingly.
|
||||
|
||||
This rule is needed so that objtool can properly identify each
|
||||
callable function in order to analyze its stack metadata.
|
||||
|
||||
2. Conversely, each section of code which is *not* callable should *not*
|
||||
be annotated as an ELF function. The ENDPROC macro shouldn't be used
|
||||
in this case.
|
||||
|
||||
This rule is needed so that objtool can ignore non-callable code.
|
||||
Such code doesn't have to follow any of the other rules.
|
||||
2. Conversely, each section of code which is *not* callable, or is
|
||||
otherwise doing funny things with the stack or registers, should
|
||||
*not* be annotated as an ELF function. Rather, SYM_CODE_{START,END}
|
||||
should be used along with unwind hints.
|
||||
|
||||
3. Each callable function which calls another function must have the
|
||||
correct frame pointer logic, if required by CONFIG_FRAME_POINTER or
|
||||
@ -221,7 +231,7 @@ To achieve the validation, objtool enforces the following rules:
|
||||
function B, the _caller_ of function A will be skipped on the stack
|
||||
trace.
|
||||
|
||||
4. Dynamic jumps and jumps to undefined symbols are only allowed if:
|
||||
4. Indirect jumps and jumps to undefined symbols are only allowed if:
|
||||
|
||||
a) the jump is part of a switch statement; or
|
||||
|
||||
@ -304,29 +314,29 @@ the objtool maintainers.
|
||||
001e 2823e: 80 ce 02 or $0x2,%dh
|
||||
...
|
||||
|
||||
|
||||
2. file.o: warning: objtool: .text+0x53: unreachable instruction
|
||||
|
||||
Objtool couldn't find a code path to reach the instruction.
|
||||
|
||||
If the error is for an asm file, and the instruction is inside (or
|
||||
reachable from) a callable function, the function should be annotated
|
||||
with the ENTRY/ENDPROC macros (ENDPROC is the important one).
|
||||
Otherwise, the code should probably be annotated with the unwind hint
|
||||
macros in asm/unwind_hints.h so objtool and the unwinder can know the
|
||||
stack state associated with the code.
|
||||
with the SYM_FUNC_START and SYM_FUNC_END macros.
|
||||
|
||||
If you're 100% sure the code won't affect stack traces, or if you're
|
||||
a just a bad person, you can tell objtool to ignore it. See the
|
||||
"Adding exceptions" section below.
|
||||
Otherwise, SYM_CODE_START can be used. In that case the code needs
|
||||
to be annotated with unwind hint macros.
|
||||
|
||||
If it's not actually in a callable function (e.g. kernel entry code),
|
||||
change ENDPROC to END.
|
||||
If you're sure the code won't affect the reliability of runtime stack
|
||||
traces and want objtool to ignore it, see "Adding exceptions" below.
|
||||
|
||||
3. file.o: warning: objtool: foo+0x48c: bar() is missing a __noreturn annotation
|
||||
|
||||
The call from foo() to bar() doesn't return, but bar() is missing the
|
||||
__noreturn annotation. NOTE: In addition to annotating the function
|
||||
with __noreturn, please also add it to tools/objtool/noreturns.h.
|
||||
3. file.o: warning: objtool: foo+0x48c: bar() missing __noreturn in .c/.h or NORETURN() in noreturns.h
|
||||
|
||||
The call from foo() to bar() doesn't return, but bar() is incorrectly
|
||||
annotated. A noreturn function must be marked __noreturn in both its
|
||||
declaration and its definition, and must have a NORETURN() annotation
|
||||
in tools/objtool/noreturns.h.
|
||||
|
||||
|
||||
4. file.o: warning: objtool: func(): can't find starting instruction
|
||||
or
|
||||
@ -341,23 +351,21 @@ the objtool maintainers.
|
||||
|
||||
This is a kernel entry/exit instruction like sysenter or iret. Such
|
||||
instructions aren't allowed in a callable function, and are most
|
||||
likely part of the kernel entry code. They should usually not have
|
||||
the callable function annotation (ENDPROC) and should always be
|
||||
annotated with the unwind hint macros in asm/unwind_hints.h.
|
||||
likely part of the kernel entry code. Such code should probably be
|
||||
placed in a SYM_FUNC_CODE block with unwind hints.
|
||||
|
||||
|
||||
6. file.o: warning: objtool: func()+0x26: sibling call from callable instruction with modified stack frame
|
||||
|
||||
This is a dynamic jump or a jump to an undefined symbol. Objtool
|
||||
assumed it's a sibling call and detected that the frame pointer
|
||||
wasn't first restored to its original state.
|
||||
This is a branch to an UNDEF symbol. Objtool assumed it's a
|
||||
sibling call and detected that the stack wasn't first restored to its
|
||||
original state.
|
||||
|
||||
If it's not really a sibling call, you may need to move the
|
||||
destination code to the local file.
|
||||
If it's not really a sibling call, you may need to use unwind hints
|
||||
and/or move the destination code to the local file.
|
||||
|
||||
If the instruction is not actually in a callable function (e.g.
|
||||
kernel entry code), change ENDPROC to END and annotate manually with
|
||||
the unwind hint macros in asm/unwind_hints.h.
|
||||
kernel entry code), use SYM_CODE_{START,END} and unwind hints.
|
||||
|
||||
|
||||
7. file: warning: objtool: func()+0x5c: stack state mismatch
|
||||
@ -373,8 +381,8 @@ the objtool maintainers.
|
||||
|
||||
Another possibility is that the code has some asm or inline asm which
|
||||
does some unusual things to the stack or the frame pointer. In such
|
||||
cases it's probably appropriate to use the unwind hint macros in
|
||||
asm/unwind_hints.h.
|
||||
cases it's probably appropriate to use SYM_FUNC_CODE with unwind
|
||||
hints.
|
||||
|
||||
|
||||
8. file.o: warning: objtool: funcA() falls through to next function funcB()
|
||||
@ -384,17 +392,16 @@ the objtool maintainers.
|
||||
can fall through into the next function. There could be different
|
||||
reasons for this:
|
||||
|
||||
1) funcA()'s last instruction is a call to a "noreturn" function like
|
||||
a) funcA()'s last instruction is a call to a "noreturn" function like
|
||||
panic(). In this case the noreturn function needs to be added to
|
||||
objtool's hard-coded global_noreturns array. Feel free to bug the
|
||||
objtool maintainer, or you can submit a patch.
|
||||
|
||||
2) funcA() uses the unreachable() annotation in a section of code
|
||||
b) funcA() uses the unreachable() annotation in a section of code
|
||||
that is actually reachable.
|
||||
|
||||
3) If funcA() calls an inline function, the object code for funcA()
|
||||
might be corrupt due to a gcc bug. For more details, see:
|
||||
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646
|
||||
c) Some undefined behavior like divide by zero.
|
||||
|
||||
|
||||
9. file.o: warning: objtool: funcA() call to funcB() with UACCESS enabled
|
||||
|
||||
@ -432,24 +439,26 @@ the objtool maintainers.
|
||||
This limitation can be overcome by massaging the alternatives with
|
||||
NOPs to shift the stack changes around so they no longer conflict.
|
||||
|
||||
|
||||
11. file.o: warning: unannotated intra-function call
|
||||
|
||||
This warning means that a direct call is done to a destination which
|
||||
is not at the beginning of a function. If this is a legit call, you
|
||||
can remove this warning by putting the ANNOTATE_INTRA_FUNCTION_CALL
|
||||
directive right before the call.
|
||||
This warning means that a direct call is done to a destination which
|
||||
is not at the beginning of a function. If this is a legit call, you
|
||||
can remove this warning by putting the ANNOTATE_INTRA_FUNCTION_CALL
|
||||
directive right before the call.
|
||||
|
||||
|
||||
12. file.o: warning: func(): not an indirect call target
|
||||
|
||||
This means that objtool is running with --ibt and a function expected
|
||||
to be an indirect call target is not. In particular, this happens for
|
||||
init_module() or cleanup_module() if a module relies on these special
|
||||
names and does not use module_init() / module_exit() macros to create
|
||||
them.
|
||||
This means that objtool is running with --ibt and a function
|
||||
expected to be an indirect call target is not. In particular, this
|
||||
happens for init_module() or cleanup_module() if a module relies on
|
||||
these special names and does not use module_init() / module_exit()
|
||||
macros to create them.
|
||||
|
||||
|
||||
If the error doesn't seem to make sense, it could be a bug in objtool.
|
||||
Feel free to ask the objtool maintainer for help.
|
||||
Feel free to ask objtool maintainers for help.
|
||||
|
||||
|
||||
Adding exceptions
|
||||
|
@ -37,7 +37,7 @@ OBJTOOL_CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBE
|
||||
OBJTOOL_LDFLAGS := $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS)
|
||||
|
||||
# Allow old libelf to be used:
|
||||
elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - | grep elf_getshdr)
|
||||
elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - 2>/dev/null | grep elf_getshdr)
|
||||
OBJTOOL_CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED)
|
||||
|
||||
# Always want host compilation.
|
||||
|
@ -5,10 +5,7 @@
|
||||
#include <asm/inst.h>
|
||||
#include <asm/orc_types.h>
|
||||
#include <linux/objtool_types.h>
|
||||
|
||||
#ifndef EM_LOONGARCH
|
||||
#define EM_LOONGARCH 258
|
||||
#endif
|
||||
#include <arch/elf.h>
|
||||
|
||||
int arch_ftrace_match(char *name)
|
||||
{
|
||||
@ -363,3 +360,26 @@ void arch_initial_func_cfi_state(struct cfi_init_state *state)
|
||||
state->cfa.base = CFI_SP;
|
||||
state->cfa.offset = 0;
|
||||
}
|
||||
|
||||
unsigned int arch_reloc_size(struct reloc *reloc)
|
||||
{
|
||||
switch (reloc_type(reloc)) {
|
||||
case R_LARCH_32:
|
||||
case R_LARCH_32_PCREL:
|
||||
return 4;
|
||||
default:
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table)
|
||||
{
|
||||
switch (reloc_type(reloc)) {
|
||||
case R_LARCH_32_PCREL:
|
||||
case R_LARCH_64_PCREL:
|
||||
return reloc->sym->offset + reloc_addend(reloc) -
|
||||
(reloc_offset(reloc) - reloc_offset(table));
|
||||
default:
|
||||
return reloc->sym->offset + reloc_addend(reloc);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,13 @@
|
||||
#ifndef R_LARCH_32_PCREL
|
||||
#define R_LARCH_32_PCREL 99
|
||||
#endif
|
||||
#ifndef R_LARCH_64_PCREL
|
||||
#define R_LARCH_64_PCREL 109
|
||||
#endif
|
||||
|
||||
#ifndef EM_LOONGARCH
|
||||
#define EM_LOONGARCH 258
|
||||
#endif
|
||||
|
||||
#define R_NONE R_LARCH_NONE
|
||||
#define R_ABS32 R_LARCH_32
|
||||
|
@ -1,5 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <string.h>
|
||||
#include <objtool/special.h>
|
||||
#include <objtool/warn.h>
|
||||
|
||||
bool arch_support_alt_relocation(struct special_alt *special_alt,
|
||||
struct instruction *insn,
|
||||
@ -8,9 +10,164 @@ bool arch_support_alt_relocation(struct special_alt *special_alt,
|
||||
return false;
|
||||
}
|
||||
|
||||
struct table_info {
|
||||
struct list_head jump_info;
|
||||
unsigned long insn_offset;
|
||||
unsigned long rodata_offset;
|
||||
};
|
||||
|
||||
static void get_rodata_table_size_by_table_annotate(struct objtool_file *file,
|
||||
struct instruction *insn,
|
||||
unsigned long *table_size)
|
||||
{
|
||||
struct section *rsec;
|
||||
struct reloc *reloc;
|
||||
struct list_head table_list;
|
||||
struct table_info *orig_table;
|
||||
struct table_info *next_table;
|
||||
unsigned long tmp_insn_offset;
|
||||
unsigned long tmp_rodata_offset;
|
||||
|
||||
rsec = find_section_by_name(file->elf, ".rela.discard.tablejump_annotate");
|
||||
if (!rsec)
|
||||
return;
|
||||
|
||||
INIT_LIST_HEAD(&table_list);
|
||||
|
||||
for_each_reloc(rsec, reloc) {
|
||||
orig_table = malloc(sizeof(struct table_info));
|
||||
if (!orig_table) {
|
||||
WARN("malloc failed");
|
||||
return;
|
||||
}
|
||||
|
||||
orig_table->insn_offset = reloc->sym->offset + reloc_addend(reloc);
|
||||
reloc++;
|
||||
orig_table->rodata_offset = reloc->sym->offset + reloc_addend(reloc);
|
||||
|
||||
list_add_tail(&orig_table->jump_info, &table_list);
|
||||
|
||||
if (reloc_idx(reloc) + 1 == sec_num_entries(rsec))
|
||||
break;
|
||||
}
|
||||
|
||||
list_for_each_entry(orig_table, &table_list, jump_info) {
|
||||
next_table = list_next_entry(orig_table, jump_info);
|
||||
list_for_each_entry_from(next_table, &table_list, jump_info) {
|
||||
if (next_table->rodata_offset < orig_table->rodata_offset) {
|
||||
tmp_insn_offset = next_table->insn_offset;
|
||||
tmp_rodata_offset = next_table->rodata_offset;
|
||||
next_table->insn_offset = orig_table->insn_offset;
|
||||
next_table->rodata_offset = orig_table->rodata_offset;
|
||||
orig_table->insn_offset = tmp_insn_offset;
|
||||
orig_table->rodata_offset = tmp_rodata_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(orig_table, &table_list, jump_info) {
|
||||
if (insn->offset == orig_table->insn_offset) {
|
||||
next_table = list_next_entry(orig_table, jump_info);
|
||||
if (&next_table->jump_info == &table_list) {
|
||||
*table_size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
while (next_table->rodata_offset == orig_table->rodata_offset) {
|
||||
next_table = list_next_entry(next_table, jump_info);
|
||||
if (&next_table->jump_info == &table_list) {
|
||||
*table_size = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*table_size = next_table->rodata_offset - orig_table->rodata_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct reloc *find_reloc_by_table_annotate(struct objtool_file *file,
|
||||
struct instruction *insn,
|
||||
unsigned long *table_size)
|
||||
{
|
||||
struct section *rsec;
|
||||
struct reloc *reloc;
|
||||
unsigned long offset;
|
||||
|
||||
rsec = find_section_by_name(file->elf, ".rela.discard.tablejump_annotate");
|
||||
if (!rsec)
|
||||
return NULL;
|
||||
|
||||
for_each_reloc(rsec, reloc) {
|
||||
if (reloc->sym->sec->rodata)
|
||||
continue;
|
||||
|
||||
if (strcmp(insn->sec->name, reloc->sym->sec->name))
|
||||
continue;
|
||||
|
||||
offset = reloc->sym->offset + reloc_addend(reloc);
|
||||
if (insn->offset == offset) {
|
||||
get_rodata_table_size_by_table_annotate(file, insn, table_size);
|
||||
reloc++;
|
||||
return reloc;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct reloc *find_reloc_of_rodata_c_jump_table(struct section *sec,
|
||||
unsigned long offset,
|
||||
unsigned long *table_size)
|
||||
{
|
||||
struct section *rsec;
|
||||
struct reloc *reloc;
|
||||
|
||||
rsec = sec->rsec;
|
||||
if (!rsec)
|
||||
return NULL;
|
||||
|
||||
for_each_reloc(rsec, reloc) {
|
||||
if (reloc_offset(reloc) > offset)
|
||||
break;
|
||||
|
||||
if (!strcmp(reloc->sym->sec->name, C_JUMP_TABLE_SECTION)) {
|
||||
*table_size = 0;
|
||||
return reloc;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct reloc *arch_find_switch_table(struct objtool_file *file,
|
||||
struct instruction *insn,
|
||||
unsigned long *table_size)
|
||||
{
|
||||
return NULL;
|
||||
struct reloc *annotate_reloc;
|
||||
struct reloc *rodata_reloc;
|
||||
struct section *table_sec;
|
||||
unsigned long table_offset;
|
||||
|
||||
annotate_reloc = find_reloc_by_table_annotate(file, insn, table_size);
|
||||
if (!annotate_reloc) {
|
||||
annotate_reloc = find_reloc_of_rodata_c_jump_table(
|
||||
insn->sec, insn->offset, table_size);
|
||||
if (!annotate_reloc)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
table_sec = annotate_reloc->sym->sec;
|
||||
table_offset = annotate_reloc->sym->offset + reloc_addend(annotate_reloc);
|
||||
|
||||
/*
|
||||
* Each table entry has a rela associated with it. The rela
|
||||
* should reference text in the same function as the original
|
||||
* instruction.
|
||||
*/
|
||||
rodata_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset);
|
||||
if (!rodata_reloc)
|
||||
return NULL;
|
||||
|
||||
return rodata_reloc;
|
||||
}
|
||||
|
@ -106,3 +106,17 @@ void arch_initial_func_cfi_state(struct cfi_init_state *state)
|
||||
state->regs[CFI_RA].base = CFI_CFA;
|
||||
state->regs[CFI_RA].offset = 0;
|
||||
}
|
||||
|
||||
unsigned int arch_reloc_size(struct reloc *reloc)
|
||||
{
|
||||
switch (reloc_type(reloc)) {
|
||||
case R_PPC_REL32:
|
||||
case R_PPC_ADDR32:
|
||||
case R_PPC_UADDR32:
|
||||
case R_PPC_PLT32:
|
||||
case R_PPC_PLTREL32:
|
||||
return 4;
|
||||
default:
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
@ -852,3 +852,16 @@ bool arch_is_embedded_insn(struct symbol *sym)
|
||||
return !strcmp(sym->name, "retbleed_return_thunk") ||
|
||||
!strcmp(sym->name, "srso_safe_ret");
|
||||
}
|
||||
|
||||
unsigned int arch_reloc_size(struct reloc *reloc)
|
||||
{
|
||||
switch (reloc_type(reloc)) {
|
||||
case R_X86_64_32:
|
||||
case R_X86_64_32S:
|
||||
case R_X86_64_PC32:
|
||||
case R_X86_64_PLT32:
|
||||
return 4;
|
||||
default:
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,10 @@
|
||||
#include <subcmd/parse-options.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <objtool/builtin.h>
|
||||
#include <objtool/objtool.h>
|
||||
|
||||
@ -14,6 +18,8 @@
|
||||
"error: objtool: " format "\n", \
|
||||
##__VA_ARGS__)
|
||||
|
||||
const char *objname;
|
||||
|
||||
struct opts opts;
|
||||
|
||||
static const char * const check_usage[] = {
|
||||
@ -71,7 +77,7 @@ static const struct option check_options[] = {
|
||||
OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),
|
||||
OPT_BOOLEAN('m', "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"),
|
||||
OPT_BOOLEAN('n', "noinstr", &opts.noinstr, "validate noinstr rules"),
|
||||
OPT_BOOLEAN('o', "orc", &opts.orc, "generate ORC metadata"),
|
||||
OPT_BOOLEAN(0, "orc", &opts.orc, "generate ORC metadata"),
|
||||
OPT_BOOLEAN('r', "retpoline", &opts.retpoline, "validate and annotate retpoline usage"),
|
||||
OPT_BOOLEAN(0, "rethunk", &opts.rethunk, "validate and annotate rethunk usage"),
|
||||
OPT_BOOLEAN(0, "unret", &opts.unret, "validate entry unret placement"),
|
||||
@ -84,16 +90,17 @@ static const struct option check_options[] = {
|
||||
OPT_CALLBACK_OPTARG(0, "dump", NULL, NULL, "orc", "dump metadata", parse_dump),
|
||||
|
||||
OPT_GROUP("Options:"),
|
||||
OPT_BOOLEAN(0, "backtrace", &opts.backtrace, "unwind on error"),
|
||||
OPT_BOOLEAN(0, "backup", &opts.backup, "create .orig files before modification"),
|
||||
OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"),
|
||||
OPT_BOOLEAN(0, "link", &opts.link, "object is a linked object"),
|
||||
OPT_BOOLEAN(0, "module", &opts.module, "object is part of a kernel module"),
|
||||
OPT_BOOLEAN(0, "mnop", &opts.mnop, "nop out mcount call sites"),
|
||||
OPT_BOOLEAN(0, "no-unreachable", &opts.no_unreachable, "skip 'unreachable instruction' warnings"),
|
||||
OPT_BOOLEAN(0, "sec-address", &opts.sec_address, "print section addresses in warnings"),
|
||||
OPT_BOOLEAN(0, "stats", &opts.stats, "print statistics"),
|
||||
OPT_BOOLEAN(0, "backtrace", &opts.backtrace, "unwind on error"),
|
||||
OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"),
|
||||
OPT_BOOLEAN(0, "link", &opts.link, "object is a linked object"),
|
||||
OPT_BOOLEAN(0, "module", &opts.module, "object is part of a kernel module"),
|
||||
OPT_BOOLEAN(0, "mnop", &opts.mnop, "nop out mcount call sites"),
|
||||
OPT_BOOLEAN(0, "no-unreachable", &opts.no_unreachable, "skip 'unreachable instruction' warnings"),
|
||||
OPT_STRING('o', "output", &opts.output, "file", "output file name"),
|
||||
OPT_BOOLEAN(0, "sec-address", &opts.sec_address, "print section addresses in warnings"),
|
||||
OPT_BOOLEAN(0, "stats", &opts.stats, "print statistics"),
|
||||
OPT_BOOLEAN('v', "verbose", &opts.verbose, "verbose warnings"),
|
||||
OPT_BOOLEAN(0, "Werror", &opts.werror, "return error on warnings"),
|
||||
|
||||
OPT_END(),
|
||||
};
|
||||
@ -131,6 +138,26 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[])
|
||||
|
||||
static bool opts_valid(void)
|
||||
{
|
||||
if (opts.mnop && !opts.mcount) {
|
||||
ERROR("--mnop requires --mcount");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.noinstr && !opts.link) {
|
||||
ERROR("--noinstr requires --link");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.ibt && !opts.link) {
|
||||
ERROR("--ibt requires --link");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.unret && !opts.link) {
|
||||
ERROR("--unret requires --link");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.hack_jump_label ||
|
||||
opts.hack_noinstr ||
|
||||
opts.ibt ||
|
||||
@ -151,11 +178,6 @@ static bool opts_valid(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (opts.unret && !opts.rethunk) {
|
||||
ERROR("--unret requires --rethunk");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.dump_orc)
|
||||
return true;
|
||||
|
||||
@ -163,76 +185,156 @@ static bool opts_valid(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool mnop_opts_valid(void)
|
||||
static int copy_file(const char *src, const char *dst)
|
||||
{
|
||||
if (opts.mnop && !opts.mcount) {
|
||||
ERROR("--mnop requires --mcount");
|
||||
return false;
|
||||
size_t to_copy, copied;
|
||||
int dst_fd, src_fd;
|
||||
struct stat stat;
|
||||
off_t offset = 0;
|
||||
|
||||
src_fd = open(src, O_RDONLY);
|
||||
if (src_fd == -1) {
|
||||
ERROR("can't open '%s' for reading", src);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0400);
|
||||
if (dst_fd == -1) {
|
||||
ERROR("can't open '%s' for writing", dst);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fstat(src_fd, &stat) == -1) {
|
||||
perror("fstat");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fchmod(dst_fd, stat.st_mode) == -1) {
|
||||
perror("fchmod");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (to_copy = stat.st_size; to_copy > 0; to_copy -= copied) {
|
||||
copied = sendfile(dst_fd, src_fd, &offset, to_copy);
|
||||
if (copied == -1) {
|
||||
perror("sendfile");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
close(dst_fd);
|
||||
close(src_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool link_opts_valid(struct objtool_file *file)
|
||||
static char **save_argv(int argc, const char **argv)
|
||||
{
|
||||
if (opts.link)
|
||||
return true;
|
||||
char **orig_argv;
|
||||
|
||||
if (has_multiple_files(file->elf)) {
|
||||
ERROR("Linked object detected, forcing --link");
|
||||
opts.link = true;
|
||||
return true;
|
||||
orig_argv = calloc(argc, sizeof(char *));
|
||||
if (!orig_argv) {
|
||||
perror("calloc");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (opts.noinstr) {
|
||||
ERROR("--noinstr requires --link");
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < argc; i++) {
|
||||
orig_argv[i] = strdup(argv[i]);
|
||||
if (!orig_argv[i]) {
|
||||
perror("strdup");
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
if (opts.ibt) {
|
||||
ERROR("--ibt requires --link");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.unret) {
|
||||
ERROR("--unret requires --link");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return orig_argv;
|
||||
}
|
||||
|
||||
#define ORIG_SUFFIX ".orig"
|
||||
|
||||
int objtool_run(int argc, const char **argv)
|
||||
{
|
||||
const char *objname;
|
||||
struct objtool_file *file;
|
||||
int ret;
|
||||
char *backup = NULL;
|
||||
char **orig_argv;
|
||||
int ret = 0;
|
||||
|
||||
argc = cmd_parse_options(argc, argv, check_usage);
|
||||
objname = argv[0];
|
||||
orig_argv = save_argv(argc, argv);
|
||||
if (!orig_argv)
|
||||
return 1;
|
||||
|
||||
cmd_parse_options(argc, argv, check_usage);
|
||||
|
||||
if (!opts_valid())
|
||||
return 1;
|
||||
|
||||
objname = argv[0];
|
||||
|
||||
if (opts.dump_orc)
|
||||
return orc_dump(objname);
|
||||
|
||||
if (!opts.dryrun && opts.output) {
|
||||
/* copy original .o file to output file */
|
||||
if (copy_file(objname, opts.output))
|
||||
return 1;
|
||||
|
||||
/* from here on, work directly on the output file */
|
||||
objname = opts.output;
|
||||
}
|
||||
|
||||
file = objtool_open_read(objname);
|
||||
if (!file)
|
||||
return 1;
|
||||
goto err;
|
||||
|
||||
if (!mnop_opts_valid())
|
||||
return 1;
|
||||
|
||||
if (!link_opts_valid(file))
|
||||
return 1;
|
||||
if (!opts.link && has_multiple_files(file->elf)) {
|
||||
ERROR("Linked object requires --link");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = check(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err;
|
||||
|
||||
if (file->elf->changed)
|
||||
return elf_write(file->elf);
|
||||
if (!opts.dryrun && file->elf->changed && elf_write(file->elf))
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (opts.dryrun)
|
||||
goto err_msg;
|
||||
|
||||
if (opts.output) {
|
||||
unlink(opts.output);
|
||||
goto err_msg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a backup before kbuild deletes the file so the error
|
||||
* can be recreated without recompiling or relinking.
|
||||
*/
|
||||
backup = malloc(strlen(objname) + strlen(ORIG_SUFFIX) + 1);
|
||||
if (!backup) {
|
||||
perror("malloc");
|
||||
return 1;
|
||||
}
|
||||
|
||||
strcpy(backup, objname);
|
||||
strcat(backup, ORIG_SUFFIX);
|
||||
if (copy_file(objname, backup))
|
||||
return 1;
|
||||
|
||||
err_msg:
|
||||
fprintf(stderr, "%s", orig_argv[0]);
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
char *arg = orig_argv[i];
|
||||
|
||||
if (backup && !strcmp(arg, objname))
|
||||
fprintf(stderr, " %s -o %s", backup, objname);
|
||||
else
|
||||
fprintf(stderr, " %s", arg);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -1944,6 +1944,11 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
__weak unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table)
|
||||
{
|
||||
return reloc->sym->offset + reloc_addend(reloc);
|
||||
}
|
||||
|
||||
static int add_jump_table(struct objtool_file *file, struct instruction *insn,
|
||||
struct reloc *next_table)
|
||||
{
|
||||
@ -1954,6 +1959,7 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
|
||||
unsigned int prev_offset = 0;
|
||||
struct reloc *reloc = table;
|
||||
struct alternative *alt;
|
||||
unsigned long sym_offset;
|
||||
|
||||
/*
|
||||
* Each @reloc is a switch table relocation which points to the target
|
||||
@ -1968,12 +1974,13 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
|
||||
break;
|
||||
|
||||
/* Make sure the table entries are consecutive: */
|
||||
if (prev_offset && reloc_offset(reloc) != prev_offset + 8)
|
||||
if (prev_offset && reloc_offset(reloc) != prev_offset + arch_reloc_size(reloc))
|
||||
break;
|
||||
|
||||
sym_offset = arch_jump_table_sym_offset(reloc, table);
|
||||
|
||||
/* Detect function pointers from contiguous objects: */
|
||||
if (reloc->sym->sec == pfunc->sec &&
|
||||
reloc_addend(reloc) == pfunc->offset)
|
||||
if (reloc->sym->sec == pfunc->sec && sym_offset == pfunc->offset)
|
||||
break;
|
||||
|
||||
/*
|
||||
@ -1981,10 +1988,10 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
|
||||
* which point to the end of the function. Ignore them.
|
||||
*/
|
||||
if (reloc->sym->sec == pfunc->sec &&
|
||||
reloc_addend(reloc) == pfunc->offset + pfunc->len)
|
||||
sym_offset == pfunc->offset + pfunc->len)
|
||||
goto next;
|
||||
|
||||
dest_insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
|
||||
dest_insn = find_insn(file, reloc->sym->sec, sym_offset);
|
||||
if (!dest_insn)
|
||||
break;
|
||||
|
||||
@ -2023,6 +2030,7 @@ static void find_jump_table(struct objtool_file *file, struct symbol *func,
|
||||
struct reloc *table_reloc;
|
||||
struct instruction *dest_insn, *orig_insn = insn;
|
||||
unsigned long table_size;
|
||||
unsigned long sym_offset;
|
||||
|
||||
/*
|
||||
* Backward search using the @first_jump_src links, these help avoid
|
||||
@ -2046,7 +2054,10 @@ static void find_jump_table(struct objtool_file *file, struct symbol *func,
|
||||
table_reloc = arch_find_switch_table(file, insn, &table_size);
|
||||
if (!table_reloc)
|
||||
continue;
|
||||
dest_insn = find_insn(file, table_reloc->sym->sec, reloc_addend(table_reloc));
|
||||
|
||||
sym_offset = table_reloc->sym->offset + reloc_addend(table_reloc);
|
||||
|
||||
dest_insn = find_insn(file, table_reloc->sym->sec, sym_offset);
|
||||
if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func)
|
||||
continue;
|
||||
|
||||
@ -4449,35 +4460,6 @@ static int validate_sls(struct objtool_file *file)
|
||||
return warnings;
|
||||
}
|
||||
|
||||
static bool ignore_noreturn_call(struct instruction *insn)
|
||||
{
|
||||
struct symbol *call_dest = insn_call_dest(insn);
|
||||
|
||||
/*
|
||||
* FIXME: hack, we need a real noreturn solution
|
||||
*
|
||||
* Problem is, exc_double_fault() may or may not return, depending on
|
||||
* whether CONFIG_X86_ESPFIX64 is set. But objtool has no visibility
|
||||
* to the kernel config.
|
||||
*
|
||||
* Other potential ways to fix it:
|
||||
*
|
||||
* - have compiler communicate __noreturn functions somehow
|
||||
* - remove CONFIG_X86_ESPFIX64
|
||||
* - read the .config file
|
||||
* - add a cmdline option
|
||||
* - create a generic objtool annotation format (vs a bunch of custom
|
||||
* formats) and annotate it
|
||||
*/
|
||||
if (!strcmp(call_dest->name, "exc_double_fault")) {
|
||||
/* prevent further unreachable warnings for the caller */
|
||||
insn->sym->warned = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int validate_reachable_instructions(struct objtool_file *file)
|
||||
{
|
||||
struct instruction *insn, *prev_insn;
|
||||
@ -4494,8 +4476,8 @@ static int validate_reachable_instructions(struct objtool_file *file)
|
||||
prev_insn = prev_insn_same_sec(file, insn);
|
||||
if (prev_insn && prev_insn->dead_end) {
|
||||
call_dest = insn_call_dest(prev_insn);
|
||||
if (call_dest && !ignore_noreturn_call(prev_insn)) {
|
||||
WARN_INSN(insn, "%s() is missing a __noreturn annotation",
|
||||
if (call_dest) {
|
||||
WARN_INSN(insn, "%s() missing __noreturn in .c/.h or NORETURN() in noreturns.h",
|
||||
call_dest->name);
|
||||
warnings++;
|
||||
continue;
|
||||
@ -4565,7 +4547,7 @@ static int disas_warned_funcs(struct objtool_file *file)
|
||||
char *funcs = NULL, *tmp;
|
||||
|
||||
for_each_sym(file, sym) {
|
||||
if (sym->warned) {
|
||||
if (sym->warnings) {
|
||||
if (!funcs) {
|
||||
funcs = malloc(strlen(sym->name) + 1);
|
||||
strcpy(funcs, sym->name);
|
||||
@ -4623,8 +4605,10 @@ int check(struct objtool_file *file)
|
||||
init_cfi_state(&force_undefined_cfi);
|
||||
force_undefined_cfi.force_undefined = true;
|
||||
|
||||
if (!cfi_hash_alloc(1UL << (file->elf->symbol_bits - 3)))
|
||||
if (!cfi_hash_alloc(1UL << (file->elf->symbol_bits - 3))) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cfi_hash_add(&init_cfi);
|
||||
cfi_hash_add(&func_cfi);
|
||||
@ -4641,7 +4625,7 @@ int check(struct objtool_file *file)
|
||||
if (opts.retpoline) {
|
||||
ret = validate_retpoline(file);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out;
|
||||
warnings += ret;
|
||||
}
|
||||
|
||||
@ -4677,7 +4661,7 @@ int check(struct objtool_file *file)
|
||||
*/
|
||||
ret = validate_unrets(file);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out;
|
||||
warnings += ret;
|
||||
}
|
||||
|
||||
@ -4740,7 +4724,7 @@ int check(struct objtool_file *file)
|
||||
if (opts.prefix) {
|
||||
ret = add_prefix_symbols(file);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out;
|
||||
warnings += ret;
|
||||
}
|
||||
|
||||
@ -4772,9 +4756,18 @@ int check(struct objtool_file *file)
|
||||
|
||||
out:
|
||||
/*
|
||||
* For now, don't fail the kernel build on fatal warnings. These
|
||||
* errors are still fairly common due to the growing matrix of
|
||||
* supported toolchains and their recent pace of change.
|
||||
* CONFIG_OBJTOOL_WERROR upgrades all warnings (and errors) to actual
|
||||
* errors.
|
||||
*
|
||||
* Note that even "fatal" type errors don't actually return an error
|
||||
* without CONFIG_OBJTOOL_WERROR. That probably needs improved at some
|
||||
* point.
|
||||
*/
|
||||
if (opts.werror && (ret || warnings)) {
|
||||
if (warnings)
|
||||
WARN("%d warning(s) upgraded to errors", warnings);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1302,9 +1302,6 @@ int elf_write(struct elf *elf)
|
||||
struct section *sec;
|
||||
Elf_Scn *s;
|
||||
|
||||
if (opts.dryrun)
|
||||
return 0;
|
||||
|
||||
/* Update changed relocation sections and section headers: */
|
||||
list_for_each_entry(sec, &elf->sections, list) {
|
||||
if (sec->truncate)
|
||||
|
@ -97,4 +97,7 @@ int arch_rewrite_retpolines(struct objtool_file *file);
|
||||
|
||||
bool arch_pc_relative_reloc(struct reloc *reloc);
|
||||
|
||||
unsigned int arch_reloc_size(struct reloc *reloc);
|
||||
unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table);
|
||||
|
||||
#endif /* _ARCH_H */
|
||||
|
@ -29,15 +29,16 @@ struct opts {
|
||||
|
||||
/* options: */
|
||||
bool backtrace;
|
||||
bool backup;
|
||||
bool dryrun;
|
||||
bool link;
|
||||
bool mnop;
|
||||
bool module;
|
||||
bool no_unreachable;
|
||||
const char *output;
|
||||
bool sec_address;
|
||||
bool stats;
|
||||
bool verbose;
|
||||
bool werror;
|
||||
};
|
||||
|
||||
extern struct opts opts;
|
||||
|
@ -65,10 +65,10 @@ struct symbol {
|
||||
u8 return_thunk : 1;
|
||||
u8 fentry : 1;
|
||||
u8 profiling_func : 1;
|
||||
u8 warned : 1;
|
||||
u8 embedded_insn : 1;
|
||||
u8 local_label : 1;
|
||||
u8 frame_pointer : 1;
|
||||
u8 warnings : 2;
|
||||
struct list_head pv_target;
|
||||
struct reloc *relocs;
|
||||
};
|
||||
|
@ -43,8 +43,10 @@ static inline char *offstr(struct section *sec, unsigned long offset)
|
||||
|
||||
#define WARN(format, ...) \
|
||||
fprintf(stderr, \
|
||||
"%s: warning: objtool: " format "\n", \
|
||||
objname, ##__VA_ARGS__)
|
||||
"%s: %s: objtool: " format "\n", \
|
||||
objname, \
|
||||
opts.werror ? "error" : "warning", \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define WARN_FUNC(format, sec, offset, ...) \
|
||||
({ \
|
||||
@ -53,14 +55,22 @@ static inline char *offstr(struct section *sec, unsigned long offset)
|
||||
free(_str); \
|
||||
})
|
||||
|
||||
#define WARN_LIMIT 2
|
||||
|
||||
#define WARN_INSN(insn, format, ...) \
|
||||
({ \
|
||||
struct instruction *_insn = (insn); \
|
||||
if (!_insn->sym || !_insn->sym->warned) \
|
||||
BUILD_BUG_ON(WARN_LIMIT > 2); \
|
||||
if (!_insn->sym || _insn->sym->warnings < WARN_LIMIT) { \
|
||||
WARN_FUNC(format, _insn->sec, _insn->offset, \
|
||||
##__VA_ARGS__); \
|
||||
if (_insn->sym) \
|
||||
_insn->sym->warned = 1; \
|
||||
if (_insn->sym) \
|
||||
_insn->sym->warnings++; \
|
||||
} else if (_insn->sym && _insn->sym->warnings == WARN_LIMIT) { \
|
||||
WARN_FUNC("skipping duplicate warning(s)", \
|
||||
_insn->sec, _insn->offset); \
|
||||
_insn->sym->warnings++; \
|
||||
} \
|
||||
})
|
||||
|
||||
#define BT_INSN(insn, format, ...) \
|
||||
|
@ -18,87 +18,19 @@
|
||||
|
||||
bool help;
|
||||
|
||||
const char *objname;
|
||||
static struct objtool_file file;
|
||||
|
||||
static bool objtool_create_backup(const char *_objname)
|
||||
struct objtool_file *objtool_open_read(const char *filename)
|
||||
{
|
||||
int len = strlen(_objname);
|
||||
char *buf, *base, *name = malloc(len+6);
|
||||
int s, d, l, t;
|
||||
|
||||
if (!name) {
|
||||
perror("failed backup name malloc");
|
||||
return false;
|
||||
if (file.elf) {
|
||||
WARN("won't handle more than one file at a time");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(name, _objname);
|
||||
strcpy(name + len, ".orig");
|
||||
|
||||
d = open(name, O_CREAT|O_WRONLY|O_TRUNC, 0644);
|
||||
if (d < 0) {
|
||||
perror("failed to create backup file");
|
||||
return false;
|
||||
}
|
||||
|
||||
s = open(_objname, O_RDONLY);
|
||||
if (s < 0) {
|
||||
perror("failed to open orig file");
|
||||
return false;
|
||||
}
|
||||
|
||||
buf = malloc(4096);
|
||||
if (!buf) {
|
||||
perror("failed backup data malloc");
|
||||
return false;
|
||||
}
|
||||
|
||||
while ((l = read(s, buf, 4096)) > 0) {
|
||||
base = buf;
|
||||
do {
|
||||
t = write(d, base, l);
|
||||
if (t < 0) {
|
||||
perror("failed backup write");
|
||||
return false;
|
||||
}
|
||||
base += t;
|
||||
l -= t;
|
||||
} while (l);
|
||||
}
|
||||
|
||||
if (l < 0) {
|
||||
perror("failed backup read");
|
||||
return false;
|
||||
}
|
||||
|
||||
free(name);
|
||||
free(buf);
|
||||
close(d);
|
||||
close(s);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct objtool_file *objtool_open_read(const char *_objname)
|
||||
{
|
||||
if (objname) {
|
||||
if (strcmp(objname, _objname)) {
|
||||
WARN("won't handle more than one file at a time");
|
||||
return NULL;
|
||||
}
|
||||
return &file;
|
||||
}
|
||||
objname = _objname;
|
||||
|
||||
file.elf = elf_open_read(objname, O_RDWR);
|
||||
file.elf = elf_open_read(filename, O_RDWR);
|
||||
if (!file.elf)
|
||||
return NULL;
|
||||
|
||||
if (opts.backup && !objtool_create_backup(objname)) {
|
||||
WARN("can't create backup file");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hash_init(file.insn_hash);
|
||||
INIT_LIST_HEAD(&file.retpoline_call_list);
|
||||
INIT_LIST_HEAD(&file.return_thunk_list);
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.h>
|
||||
|
||||
int orc_dump(const char *_objname)
|
||||
int orc_dump(const char *filename)
|
||||
{
|
||||
int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0;
|
||||
struct orc_entry *orc = NULL;
|
||||
@ -26,12 +26,9 @@ int orc_dump(const char *_objname)
|
||||
Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL;
|
||||
struct elf dummy_elf = {};
|
||||
|
||||
|
||||
objname = _objname;
|
||||
|
||||
elf_version(EV_CURRENT);
|
||||
|
||||
fd = open(objname, O_RDONLY);
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
perror("open");
|
||||
return -1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user