These are objtool fixes and updates by Josh Poimboeuf, centered

around the fallout from the new CONFIG_OBJTOOL_WERROR=y feature,
 which, despite its default-off nature, increased the profile/impact
 of objtool warnings:
 
  - Improve error handling and the presentation of warnings/errors.
 
  - Revert the new summary warning line that some test-bot tools
    interpreted as new regressions.
 
  - Fix a number of objtool warnings in various drivers, core kernel
    code and architecture code. About half of them are potential
    problems related to out-of-bounds accesses or potential undefined
    behavior, the other half are additional objtool annotations.
 
  - Update objtool to latest (known) compiler quirks and
    objtool bugs triggered by compiler code generation
 
  - Misc fixes
 
 Signed-off-by: Ingo Molnar <mingo@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAmfsRJMRHG1pbmdvQGtl
 cm5lbC5vcmcACgkQEnMQ0APhK1g0YRAApiCylIv+0ucdKiDVAiI+cU7dqAggFp9h
 ULcTuuCtVkfjYzIBw6y1Iw9JeYsyngYaI0VEMmLasJPt8o93K0vwBXGArXJKoMeu
 UPcVS8N6+LqrHsWBXk919t1wgBZ7csgUxsCa1K47NKa3eCijrqI0N8PtcoYqKd+M
 tOuyEcTCTfS0E2STv6Gpdp6VfDKms3Cn4MffLbcNWJXAsd1dwzDIG8IvAHUW9yG3
 /ezVjm46thneNrRd9j/qU3mqNmhsec9NemHG7URaTznRKleWULhpmhGmcPYCh4Rj
 AqGjmPtqprPELtgezeV+LIcmIm5UWF/f+0tzzBrsRy1MiY8ED2w+J51DHsLoHg8t
 IfIkPyYX/zu9StXoRIwx/7C5NQqBlUfXGp6TuOOwzgbKOt+uRJOU6SnSQ06ZDwsa
 l2brQ+NDfvF7EvGnvi18wIM+iqMc2jSuWl0AT94ATDuAZGCyzlmwluIYmDuLfyZM
 JuYOogojt5vgHXDN6Ro3rDfK+tYckwez+Txx4oByGB3IJy75osBihtvHiYno7FgW
 KXDbiAfLZ4SlfPzqxI6PPzaj3py6hG9LICEiL0U8VecC7bZ/22BZQCpdKko+/E/Y
 PwlqCatqz/25U7GlsnfBISJO2VAyyUcbymvjnVXzZCi+IPAfeih6WcsTPJ96jxsa
 LULLCnuvmoY=
 =KkiI
 -----END PGP SIGNATURE-----

Merge tag 'objtool-urgent-2025-04-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull objtool fixes from Ingo Molnar:
 "These are objtool fixes and updates by Josh Poimboeuf, centered around
  the fallout from the new CONFIG_OBJTOOL_WERROR=y feature, which,
  despite its default-off nature, increased the profile/impact of
  objtool warnings:

   - Improve error handling and the presentation of warnings/errors

   - Revert the new summary warning line that some test-bot tools
     interpreted as new regressions

   - Fix a number of objtool warnings in various drivers, core kernel
     code and architecture code. About half of them are potential
     problems related to out-of-bounds accesses or potential undefined
     behavior, the other half are additional objtool annotations

   - Update objtool to latest (known) compiler quirks and objtool bugs
     triggered by compiler code generation

   - Misc fixes"

* tag 'objtool-urgent-2025-04-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (36 commits)
  objtool/loongarch: Add unwind hints in prepare_frametrace()
  rcu-tasks: Always inline rcu_irq_work_resched()
  context_tracking: Always inline ct_{nmi,irq}_{enter,exit}()
  sched/smt: Always inline sched_smt_active()
  objtool: Fix verbose disassembly if CROSS_COMPILE isn't set
  objtool: Change "warning:" to "error: " for fatal errors
  objtool: Always fail on fatal errors
  Revert "objtool: Increase per-function WARN_FUNC() rate limit"
  objtool: Append "()" to function name in "unexpected end of section" warning
  objtool: Ignore end-of-section jumps for KCOV/GCOV
  objtool: Silence more KCOV warnings, part 2
  objtool, drm/vmwgfx: Don't ignore vmw_send_msg() for ORC
  objtool: Fix STACK_FRAME_NON_STANDARD for cold subfunctions
  objtool: Fix segfault in ignore_unreachable_insn()
  objtool: Fix NULL printf() '%s' argument in builtin-check.c:save_argv()
  objtool, lkdtm: Obfuscate the do_nothing() pointer
  objtool, regulator: rk808: Remove potential undefined behavior in rk806_set_mode_dcdc()
  objtool, ASoC: codecs: wcd934x: Remove potential undefined behavior in wcd934x_slim_irq_handler()
  objtool, Input: cyapa - Remove undefined behavior in cyapa_update_fw_store()
  objtool, panic: Disable SMAP in __stack_chk_fail()
  ...
This commit is contained in:
Linus Torvalds 2025-04-02 10:30:10 -07:00
commit 92b71befc3
39 changed files with 679 additions and 650 deletions

View file

@ -8,6 +8,7 @@
#include <asm/asm.h>
#include <asm/ptrace.h>
#include <asm/loongarch.h>
#include <asm/unwind_hints.h>
#include <linux/stringify.h>
enum stack_type {
@ -43,6 +44,7 @@ int get_stack_info(unsigned long stack, struct task_struct *task, struct stack_i
static __always_inline void prepare_frametrace(struct pt_regs *regs)
{
__asm__ __volatile__(
UNWIND_HINT_SAVE
/* Save $ra */
STORE_ONE_REG(1)
/* Use $ra to save PC */
@ -80,6 +82,7 @@ static __always_inline void prepare_frametrace(struct pt_regs *regs)
STORE_ONE_REG(29)
STORE_ONE_REG(30)
STORE_ONE_REG(31)
UNWIND_HINT_RESTORE
: "=m" (regs->csr_era)
: "r" (regs->regs)
: "memory");

View file

@ -23,6 +23,14 @@
UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_CALL
.endm
#endif /* __ASSEMBLY__ */
#else /* !__ASSEMBLY__ */
#define UNWIND_HINT_SAVE \
UNWIND_HINT(UNWIND_HINT_TYPE_SAVE, 0, 0, 0)
#define UNWIND_HINT_RESTORE \
UNWIND_HINT(UNWIND_HINT_TYPE_RESTORE, 0, 0, 0)
#endif /* !__ASSEMBLY__ */
#endif /* _ASM_LOONGARCH_UNWIND_HINTS_H */

View file

@ -16,7 +16,8 @@ static __always_inline unsigned int __arch_hweight32(unsigned int w)
{
unsigned int res;
asm_inline (ALTERNATIVE("call __sw_hweight32",
asm_inline (ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE
"call __sw_hweight32",
"popcntl %[val], %[cnt]", X86_FEATURE_POPCNT)
: [cnt] "=" REG_OUT (res), ASM_CALL_CONSTRAINT
: [val] REG_IN (w));
@ -45,7 +46,8 @@ static __always_inline unsigned long __arch_hweight64(__u64 w)
{
unsigned long res;
asm_inline (ALTERNATIVE("call __sw_hweight64",
asm_inline (ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE
"call __sw_hweight64",
"popcntq %[val], %[cnt]", X86_FEATURE_POPCNT)
: [cnt] "=" REG_OUT (res), ASM_CALL_CONSTRAINT
: [val] REG_IN (w));

View file

@ -16,23 +16,23 @@
#ifdef __ASSEMBLER__
#define ASM_CLAC \
ALTERNATIVE "", "clac", X86_FEATURE_SMAP
ALTERNATIVE __stringify(ANNOTATE_IGNORE_ALTERNATIVE), "clac", X86_FEATURE_SMAP
#define ASM_STAC \
ALTERNATIVE "", "stac", X86_FEATURE_SMAP
ALTERNATIVE __stringify(ANNOTATE_IGNORE_ALTERNATIVE), "stac", X86_FEATURE_SMAP
#else /* __ASSEMBLER__ */
static __always_inline void clac(void)
{
/* Note: a barrier is implicit in alternative() */
alternative("", "clac", X86_FEATURE_SMAP);
alternative(ANNOTATE_IGNORE_ALTERNATIVE "", "clac", X86_FEATURE_SMAP);
}
static __always_inline void stac(void)
{
/* Note: a barrier is implicit in alternative() */
alternative("", "stac", X86_FEATURE_SMAP);
alternative(ANNOTATE_IGNORE_ALTERNATIVE "", "stac", X86_FEATURE_SMAP);
}
static __always_inline unsigned long smap_save(void)
@ -40,7 +40,8 @@ static __always_inline unsigned long smap_save(void)
unsigned long flags;
asm volatile ("# smap_save\n\t"
ALTERNATIVE("", "pushf; pop %0; " "clac" "\n\t",
ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE
"", "pushf; pop %0; clac",
X86_FEATURE_SMAP)
: "=rm" (flags) : : "memory", "cc");
@ -50,16 +51,22 @@ static __always_inline unsigned long smap_save(void)
static __always_inline void smap_restore(unsigned long flags)
{
asm volatile ("# smap_restore\n\t"
ALTERNATIVE("", "push %0; popf\n\t",
ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE
"", "push %0; popf",
X86_FEATURE_SMAP)
: : "g" (flags) : "memory", "cc");
}
/* These macros can be used in asm() statements */
#define ASM_CLAC \
ALTERNATIVE("", "clac", X86_FEATURE_SMAP)
ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE "", "clac", X86_FEATURE_SMAP)
#define ASM_STAC \
ALTERNATIVE("", "stac", X86_FEATURE_SMAP)
ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE "", "stac", X86_FEATURE_SMAP)
#define ASM_CLAC_UNSAFE \
ALTERNATIVE("", ANNOTATE_IGNORE_ALTERNATIVE "clac", X86_FEATURE_SMAP)
#define ASM_STAC_UNSAFE \
ALTERNATIVE("", ANNOTATE_IGNORE_ALTERNATIVE "stac", X86_FEATURE_SMAP)
#endif /* __ASSEMBLER__ */

View file

@ -231,14 +231,12 @@ static __always_inline void __xen_stac(void)
* Suppress objtool seeing the STAC/CLAC and getting confused about it
* calling random code with AC=1.
*/
asm volatile(ANNOTATE_IGNORE_ALTERNATIVE
ASM_STAC ::: "memory", "flags");
asm volatile(ASM_STAC_UNSAFE ::: "memory", "flags");
}
static __always_inline void __xen_clac(void)
{
asm volatile(ANNOTATE_IGNORE_ALTERNATIVE
ASM_CLAC ::: "memory", "flags");
asm volatile(ASM_CLAC_UNSAFE ::: "memory", "flags");
}
static inline long

View file

@ -289,7 +289,7 @@ static int vmw_send_msg(struct rpc_channel *channel, const char *msg)
return -EINVAL;
}
STACK_FRAME_NON_STANDARD(vmw_send_msg);
STACK_FRAME_NON_STANDARD_FP(vmw_send_msg);
/**

View file

@ -1080,8 +1080,8 @@ static ssize_t cyapa_update_fw_store(struct device *dev,
char fw_name[NAME_MAX];
int ret, error;
if (count >= NAME_MAX) {
dev_err(dev, "File name too long\n");
if (!count || count >= NAME_MAX) {
dev_err(dev, "Bad file name size\n");
return -EINVAL;
}

View file

@ -2701,8 +2701,11 @@ static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz)
u8 ratio;
if (state->revision == 0x8090) {
u32 internal = dib8000_read32(state, 23) / 1000;
ratio = 4;
unit_khz_dds_val = (1<<26) / (dib8000_read32(state, 23) / 1000);
unit_khz_dds_val = (1<<26) / (internal ?: 1);
if (offset_khz < 0)
dds = (1 << 26) - (abs_offset_khz * unit_khz_dds_val);
else

View file

@ -28,6 +28,13 @@ static const unsigned long rodata = 0xAA55AA55;
/* This is marked __ro_after_init, so it should ultimately be .rodata. */
static unsigned long ro_after_init __ro_after_init = 0x55AA5500;
/*
* This is a pointer to do_nothing() which is initialized at runtime rather
* than build time to avoid objtool IBT validation warnings caused by an
* inlined unrolled memcpy() in execute_location().
*/
static void __ro_after_init *do_nothing_ptr;
/*
* This just returns to the caller. It is designed to be copied into
* non-executable memory regions.
@ -65,13 +72,12 @@ static noinline __nocfi void execute_location(void *dst, bool write)
{
void (*func)(void);
func_desc_t fdesc;
void *do_nothing_text = dereference_function_descriptor(do_nothing);
pr_info("attempting ok execution at %px\n", do_nothing_text);
pr_info("attempting ok execution at %px\n", do_nothing_ptr);
do_nothing();
if (write == CODE_WRITE) {
memcpy(dst, do_nothing_text, EXEC_SIZE);
memcpy(dst, do_nothing_ptr, EXEC_SIZE);
flush_icache_range((unsigned long)dst,
(unsigned long)dst + EXEC_SIZE);
}
@ -267,6 +273,8 @@ static void lkdtm_ACCESS_NULL(void)
void __init lkdtm_perms_init(void)
{
do_nothing_ptr = dereference_function_descriptor(do_nothing);
/* Make sure we can write to __ro_after_init values during __init */
ro_after_init |= 0xAA;
}

View file

@ -78,7 +78,7 @@ static int nvmet_ctrl_state_show(struct seq_file *m, void *p)
bool sep = false;
int i;
for (i = 0; i < 7; i++) {
for (i = 0; i < ARRAY_SIZE(csts_state_names); i++) {
int state = BIT(i);
if (!(ctrl->csts & state))

View file

@ -270,8 +270,8 @@ static const unsigned int rk817_buck1_4_ramp_table[] = {
static int rk806_set_mode_dcdc(struct regulator_dev *rdev, unsigned int mode)
{
int rid = rdev_get_id(rdev);
int ctr_bit, reg;
unsigned int rid = rdev_get_id(rdev);
unsigned int ctr_bit, reg;
reg = RK806_POWER_FPWM_EN0 + rid / 8;
ctr_bit = rid % 8;

View file

@ -302,7 +302,7 @@ static void amd_set_spi_freq(struct amd_spi *amd_spi, u32 speed_hz)
{
unsigned int i, spd7_val, alt_spd;
for (i = 0; i < ARRAY_SIZE(amd_spi_freq); i++)
for (i = 0; i < ARRAY_SIZE(amd_spi_freq)-1; i++)
if (speed_hz >= amd_spi_freq[i].speed_hz)
break;

View file

@ -10,12 +10,12 @@ void ct_irq_exit_irqson(void);
void ct_nmi_enter(void);
void ct_nmi_exit(void);
#else
static inline void ct_irq_enter(void) { }
static inline void ct_irq_exit(void) { }
static __always_inline void ct_irq_enter(void) { }
static __always_inline void ct_irq_exit(void) { }
static inline void ct_irq_enter_irqson(void) { }
static inline void ct_irq_exit_irqson(void) { }
static inline void ct_nmi_enter(void) { }
static inline void ct_nmi_exit(void) { }
static __always_inline void ct_nmi_enter(void) { }
static __always_inline void ct_nmi_exit(void) { }
#endif
#endif

View file

@ -134,10 +134,6 @@
.size name, .-name
#endif
/* If symbol 'name' is treated as a subroutine (gets called, and returns)
* then please use ENDPROC to mark 'name' as STT_FUNC for the benefit of
* static analysis tools such as stack depth analyzer.
*/
#ifndef ENDPROC
/* deprecated, use SYM_FUNC_END */
#define ENDPROC(name) \

View file

@ -69,7 +69,7 @@
* In asm, there are two kinds of code: normal C-type callable functions and
* the rest. The normal callable functions can be called by other code, and
* don't do anything unusual with the stack. Such normal callable functions
* are annotated with the ENTRY/ENDPROC macros. Most asm code falls in this
* are annotated with SYM_FUNC_{START,END}. Most asm code falls in this
* category. In this case, no special debugging annotations are needed because
* objtool can automatically generate the ORC data for the ORC unwinder to read
* at runtime.

View file

@ -132,7 +132,7 @@ static inline void rcu_sysrq_end(void) { }
#if defined(CONFIG_NO_HZ_FULL) && (!defined(CONFIG_GENERIC_ENTRY) || !defined(CONFIG_KVM_XFER_TO_GUEST_WORK))
void rcu_irq_work_resched(void);
#else
static inline void rcu_irq_work_resched(void) { }
static __always_inline void rcu_irq_work_resched(void) { }
#endif
#ifdef CONFIG_RCU_NOCB_CPU

View file

@ -12,7 +12,7 @@ static __always_inline bool sched_smt_active(void)
return static_branch_likely(&sched_smt_present);
}
#else
static inline bool sched_smt_active(void) { return false; }
static __always_inline bool sched_smt_active(void) { return false; }
#endif
void arch_smt_update(void);

View file

@ -833,9 +833,15 @@ device_initcall(register_warn_debugfs);
*/
__visible noinstr void __stack_chk_fail(void)
{
unsigned long flags;
instrumentation_begin();
flags = user_access_save();
panic("stack-protector: Kernel stack is corrupted in: %pB",
__builtin_return_address(0));
user_access_restore(flags);
instrumentation_end();
}
EXPORT_SYMBOL(__stack_chk_fail);

View file

@ -275,9 +275,9 @@ objtool-args-$(CONFIG_MITIGATION_SLS) += --sls
objtool-args-$(CONFIG_STACK_VALIDATION) += --stackval
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-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable
objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror --backtrace
objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror
objtool-args = $(objtool-args-y) \
$(if $(delay-objtool), --link) \

View file

@ -30,13 +30,20 @@ endif
# objtool for vmlinux.o
# ---------------------------------------------------------------------------
#
# For LTO and IBT, objtool doesn't run on individual translation units.
# Run everything on vmlinux instead.
# For delay-objtool (IBT or LTO), objtool doesn't run on individual translation
# units. Instead it runs on vmlinux.o.
#
# For !delay-objtool + CONFIG_NOINSTR_VALIDATION, it runs on both translation
# units and vmlinux.o, with the latter only used for noinstr/unret validation.
objtool-enabled := $(or $(delay-objtool),$(CONFIG_NOINSTR_VALIDATION))
vmlinux-objtool-args-$(delay-objtool) += $(objtool-args-y)
vmlinux-objtool-args-$(CONFIG_GCOV_KERNEL) += --no-unreachable
ifeq ($(delay-objtool),y)
vmlinux-objtool-args-y += $(objtool-args-y)
else
vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror
endif
vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION) += --noinstr \
$(if $(or $(CONFIG_MITIGATION_UNRET_ENTRY),$(CONFIG_MITIGATION_SRSO)), --unret)

View file

@ -2263,7 +2263,7 @@ static irqreturn_t wcd934x_slim_irq_handler(int irq, void *data)
{
struct wcd934x_codec *wcd = data;
unsigned long status = 0;
int i, j, port_id;
unsigned int i, j, port_id;
unsigned int val, int_val = 0;
irqreturn_t ret = IRQ_NONE;
bool tx;

View file

@ -34,7 +34,7 @@ Objtool has the following features:
- 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
- Return thunk untraining validation -- validate that all entry paths
untrain a "safe return" before the first return (or call)
- Non-instrumentation validation -- validates non-instrumentable
@ -281,8 +281,8 @@ the objtool maintainers.
If the error is for an asm file, and func() is indeed a callable
function, add proper frame pointer logic using the FRAME_BEGIN and
FRAME_END macros. Otherwise, if it's not a callable function, remove
its ELF function annotation by changing ENDPROC to END, and instead
use the manual unwind hint macros in asm/unwind_hints.h.
its ELF function annotation by using SYM_CODE_{START,END} and use the
manual unwind hint macros in asm/unwind_hints.h.
If it's a GCC-compiled .c file, the error may be because the function
uses an inline asm() statement which has a "call" instruction. An
@ -352,7 +352,7 @@ 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. Such code should probably be
placed in a SYM_FUNC_CODE block with unwind hints.
placed in a SYM_CODE_{START,END} block with unwind hints.
6. file.o: warning: objtool: func()+0x26: sibling call from callable instruction with modified stack frame
@ -381,7 +381,7 @@ 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 SYM_FUNC_CODE with unwind
cases it's probably appropriate to use SYM_CODE_{START,END} with unwind
hints.

View file

@ -63,7 +63,7 @@ static bool is_loongarch(const struct elf *elf)
if (elf->ehdr.e_machine == EM_LOONGARCH)
return true;
WARN("unexpected ELF machine type %d", elf->ehdr.e_machine);
ERROR("unexpected ELF machine type %d", elf->ehdr.e_machine);
return false;
}
@ -327,8 +327,10 @@ const char *arch_nop_insn(int len)
{
static u32 nop;
if (len != LOONGARCH_INSN_SIZE)
WARN("invalid NOP size: %d\n", len);
if (len != LOONGARCH_INSN_SIZE) {
ERROR("invalid NOP size: %d\n", len);
return NULL;
}
nop = LOONGARCH_INSN_NOP;
@ -339,8 +341,10 @@ const char *arch_ret_insn(int len)
{
static u32 ret;
if (len != LOONGARCH_INSN_SIZE)
WARN("invalid RET size: %d\n", len);
if (len != LOONGARCH_INSN_SIZE) {
ERROR("invalid RET size: %d\n", len);
return NULL;
}
emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0);

View file

@ -41,7 +41,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct
orc->type = ORC_TYPE_REGS_PARTIAL;
break;
default:
WARN_INSN(insn, "unknown unwind hint type %d", cfi->type);
ERROR_INSN(insn, "unknown unwind hint type %d", cfi->type);
return -1;
}
@ -55,7 +55,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct
orc->sp_reg = ORC_REG_FP;
break;
default:
WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base);
ERROR_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base);
return -1;
}
@ -72,7 +72,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct
orc->fp_reg = ORC_REG_FP;
break;
default:
WARN_INSN(insn, "unknown FP base reg %d", fp->base);
ERROR_INSN(insn, "unknown FP base reg %d", fp->base);
return -1;
}
@ -89,7 +89,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct
orc->ra_reg = ORC_REG_FP;
break;
default:
WARN_INSN(insn, "unknown RA base reg %d", ra->base);
ERROR_INSN(insn, "unknown RA base reg %d", ra->base);
return -1;
}

View file

@ -36,7 +36,7 @@ static int is_x86_64(const struct elf *elf)
case EM_386:
return 0;
default:
WARN("unexpected ELF machine type %d", elf->ehdr.e_machine);
ERROR("unexpected ELF machine type %d", elf->ehdr.e_machine);
return -1;
}
}
@ -173,7 +173,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
ret = insn_decode(&ins, sec->data->d_buf + offset, maxlen,
x86_64 ? INSN_MODE_64 : INSN_MODE_32);
if (ret < 0) {
WARN("can't decode instruction at %s:0x%lx", sec->name, offset);
ERROR("can't decode instruction at %s:0x%lx", sec->name, offset);
return -1;
}
@ -321,7 +321,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
break;
default:
/* WARN ? */
/* ERROR ? */
break;
}
@ -561,8 +561,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
if (ins.prefixes.nbytes == 1 &&
ins.prefixes.bytes[0] == 0xf2) {
/* ENQCMD cannot be used in the kernel. */
WARN("ENQCMD instruction at %s:%lx", sec->name,
offset);
WARN("ENQCMD instruction at %s:%lx", sec->name, offset);
}
} else if (op2 == 0xa0 || op2 == 0xa8) {
@ -646,7 +645,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
if (disp->sym->type == STT_SECTION)
func = find_symbol_by_offset(disp->sym->sec, reloc_addend(disp));
if (!func) {
WARN("no func for pv_ops[]");
ERROR("no func for pv_ops[]");
return -1;
}
@ -776,7 +775,7 @@ const char *arch_nop_insn(int len)
};
if (len < 1 || len > 5) {
WARN("invalid NOP size: %d\n", len);
ERROR("invalid NOP size: %d\n", len);
return NULL;
}
@ -796,7 +795,7 @@ const char *arch_ret_insn(int len)
};
if (len < 1 || len > 5) {
WARN("invalid RET size: %d\n", len);
ERROR("invalid RET size: %d\n", len);
return NULL;
}

View file

@ -40,7 +40,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct
orc->type = ORC_TYPE_REGS_PARTIAL;
break;
default:
WARN_INSN(insn, "unknown unwind hint type %d", cfi->type);
ERROR_INSN(insn, "unknown unwind hint type %d", cfi->type);
return -1;
}
@ -72,7 +72,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct
orc->sp_reg = ORC_REG_DX;
break;
default:
WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base);
ERROR_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base);
return -1;
}
@ -87,7 +87,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct
orc->bp_reg = ORC_REG_BP;
break;
default:
WARN_INSN(insn, "unknown BP base reg %d", bp->base);
ERROR_INSN(insn, "unknown BP base reg %d", bp->base);
return -1;
}

View file

@ -3,11 +3,9 @@
#include <objtool/special.h>
#include <objtool/builtin.h>
#include <objtool/warn.h>
#define X86_FEATURE_POPCNT (4 * 32 + 23)
#define X86_FEATURE_SMAP (9 * 32 + 20)
void arch_handle_alternative(unsigned short feature, struct special_alt *alt)
void arch_handle_alternative(struct special_alt *alt)
{
static struct special_alt *group, *prev;
@ -31,34 +29,6 @@ void arch_handle_alternative(unsigned short feature, struct special_alt *alt)
} else group = alt;
prev = alt;
switch (feature) {
case X86_FEATURE_SMAP:
/*
* If UACCESS validation is enabled; force that alternative;
* otherwise force it the other way.
*
* What we want to avoid is having both the original and the
* alternative code flow at the same time, in that case we can
* find paths that see the STAC but take the NOP instead of
* CLAC and the other way around.
*/
if (opts.uaccess)
alt->skip_orig = true;
else
alt->skip_alt = true;
break;
case X86_FEATURE_POPCNT:
/*
* It has been requested that we don't validate the !POPCNT
* feature path which is a "very very small percentage of
* machines".
*/
alt->skip_orig = true;
break;
default:
break;
}
}
bool arch_support_alt_relocation(struct special_alt *special_alt,
@ -156,8 +126,10 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
* indicates a rare GCC quirk/bug which can leave dead
* code behind.
*/
if (reloc_type(text_reloc) == R_X86_64_PC32)
if (reloc_type(text_reloc) == R_X86_64_PC32) {
WARN_INSN(insn, "ignoring unreachables due to jump table quirk");
file->ignore_unreachables = true;
}
*table_size = 0;
return rodata_reloc;

View file

@ -8,18 +8,18 @@
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <objtool/builtin.h>
#include <objtool/objtool.h>
#include <objtool/warn.h>
#define ERROR(format, ...) \
fprintf(stderr, \
"error: objtool: " format "\n", \
##__VA_ARGS__)
#define ORIG_SUFFIX ".orig"
int orig_argc;
static char **orig_argv;
const char *objname;
struct opts opts;
static const char * const check_usage[] = {
@ -194,30 +194,30 @@ static int copy_file(const char *src, const char *dst)
src_fd = open(src, O_RDONLY);
if (src_fd == -1) {
ERROR("can't open '%s' for reading", src);
ERROR("can't open %s for reading: %s", src, strerror(errno));
return 1;
}
dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0400);
if (dst_fd == -1) {
ERROR("can't open '%s' for writing", dst);
ERROR("can't open %s for writing: %s", dst, strerror(errno));
return 1;
}
if (fstat(src_fd, &stat) == -1) {
perror("fstat");
ERROR_GLIBC("fstat");
return 1;
}
if (fchmod(dst_fd, stat.st_mode) == -1) {
perror("fchmod");
ERROR_GLIBC("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");
ERROR_GLIBC("sendfile");
return 1;
}
}
@ -227,39 +227,73 @@ static int copy_file(const char *src, const char *dst)
return 0;
}
static char **save_argv(int argc, const char **argv)
static void save_argv(int argc, const char **argv)
{
char **orig_argv;
orig_argv = calloc(argc, sizeof(char *));
if (!orig_argv) {
perror("calloc");
return NULL;
ERROR_GLIBC("calloc");
exit(1);
}
for (int i = 0; i < argc; i++) {
orig_argv[i] = strdup(argv[i]);
if (!orig_argv[i]) {
perror("strdup");
return NULL;
ERROR_GLIBC("strdup(%s)", argv[i]);
exit(1);
}
};
return orig_argv;
}
#define ORIG_SUFFIX ".orig"
void print_args(void)
{
char *backup = NULL;
if (opts.output || opts.dryrun)
goto print;
/*
* 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) {
ERROR_GLIBC("malloc");
goto print;
}
strcpy(backup, objname);
strcat(backup, ORIG_SUFFIX);
if (copy_file(objname, backup)) {
backup = NULL;
goto print;
}
print:
/*
* Print the cmdline args to make it easier to recreate. If '--output'
* wasn't used, add it to the printed args with the backup as input.
*/
fprintf(stderr, "%s", orig_argv[0]);
for (int i = 1; i < orig_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");
}
int objtool_run(int argc, const char **argv)
{
struct objtool_file *file;
char *backup = NULL;
char **orig_argv;
int ret = 0;
orig_argv = save_argv(argc, argv);
if (!orig_argv)
return 1;
orig_argc = argc;
save_argv(argc, argv);
cmd_parse_options(argc, argv, check_usage);
@ -282,59 +316,19 @@ int objtool_run(int argc, const char **argv)
file = objtool_open_read(objname);
if (!file)
goto err;
return 1;
if (!opts.link && has_multiple_files(file->elf)) {
ERROR("Linked object requires --link");
goto err;
return 1;
}
ret = check(file);
if (ret)
goto err;
return ret;
if (!opts.dryrun && file->elf->changed && elf_write(file->elf))
goto err;
return 1;
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;
}

File diff suppressed because it is too large Load diff

View file

@ -72,17 +72,17 @@ static inline void __elf_hash_del(struct elf_hash_node *node,
obj; \
obj = elf_list_entry(obj->member.next, typeof(*(obj)), member))
#define elf_alloc_hash(name, size) \
({ \
__elf_bits(name) = max(10, ilog2(size)); \
#define elf_alloc_hash(name, size) \
({ \
__elf_bits(name) = max(10, ilog2(size)); \
__elf_table(name) = mmap(NULL, sizeof(struct elf_hash_node *) << __elf_bits(name), \
PROT_READ|PROT_WRITE, \
MAP_PRIVATE|MAP_ANON, -1, 0); \
if (__elf_table(name) == (void *)-1L) { \
WARN("mmap fail " #name); \
__elf_table(name) = NULL; \
} \
__elf_table(name); \
PROT_READ|PROT_WRITE, \
MAP_PRIVATE|MAP_ANON, -1, 0); \
if (__elf_table(name) == (void *)-1L) { \
ERROR_GLIBC("mmap fail " #name); \
__elf_table(name) = NULL; \
} \
__elf_table(name); \
})
static inline unsigned long __sym_start(struct symbol *s)
@ -316,12 +316,12 @@ static int read_sections(struct elf *elf)
int i;
if (elf_getshdrnum(elf->elf, &sections_nr)) {
WARN_ELF("elf_getshdrnum");
ERROR_ELF("elf_getshdrnum");
return -1;
}
if (elf_getshdrstrndx(elf->elf, &shstrndx)) {
WARN_ELF("elf_getshdrstrndx");
ERROR_ELF("elf_getshdrstrndx");
return -1;
}
@ -331,7 +331,7 @@ static int read_sections(struct elf *elf)
elf->section_data = calloc(sections_nr, sizeof(*sec));
if (!elf->section_data) {
perror("calloc");
ERROR_GLIBC("calloc");
return -1;
}
for (i = 0; i < sections_nr; i++) {
@ -341,33 +341,32 @@ static int read_sections(struct elf *elf)
s = elf_getscn(elf->elf, i);
if (!s) {
WARN_ELF("elf_getscn");
ERROR_ELF("elf_getscn");
return -1;
}
sec->idx = elf_ndxscn(s);
if (!gelf_getshdr(s, &sec->sh)) {
WARN_ELF("gelf_getshdr");
ERROR_ELF("gelf_getshdr");
return -1;
}
sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name);
if (!sec->name) {
WARN_ELF("elf_strptr");
ERROR_ELF("elf_strptr");
return -1;
}
if (sec->sh.sh_size != 0 && !is_dwarf_section(sec)) {
sec->data = elf_getdata(s, NULL);
if (!sec->data) {
WARN_ELF("elf_getdata");
ERROR_ELF("elf_getdata");
return -1;
}
if (sec->data->d_off != 0 ||
sec->data->d_size != sec->sh.sh_size) {
WARN("unexpected data attributes for %s",
sec->name);
ERROR("unexpected data attributes for %s", sec->name);
return -1;
}
}
@ -387,7 +386,7 @@ static int read_sections(struct elf *elf)
/* sanity check, one more call to elf_nextscn() should return NULL */
if (elf_nextscn(elf->elf, s)) {
WARN("section entry mismatch");
ERROR("section entry mismatch");
return -1;
}
@ -467,7 +466,7 @@ static int read_symbols(struct elf *elf)
elf->symbol_data = calloc(symbols_nr, sizeof(*sym));
if (!elf->symbol_data) {
perror("calloc");
ERROR_GLIBC("calloc");
return -1;
}
for (i = 0; i < symbols_nr; i++) {
@ -477,14 +476,14 @@ static int read_symbols(struct elf *elf)
if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym,
&shndx)) {
WARN_ELF("gelf_getsymshndx");
ERROR_ELF("gelf_getsymshndx");
goto err;
}
sym->name = elf_strptr(elf->elf, symtab->sh.sh_link,
sym->sym.st_name);
if (!sym->name) {
WARN_ELF("elf_strptr");
ERROR_ELF("elf_strptr");
goto err;
}
@ -496,8 +495,7 @@ static int read_symbols(struct elf *elf)
sym->sec = find_section_by_index(elf, shndx);
if (!sym->sec) {
WARN("couldn't find section for symbol %s",
sym->name);
ERROR("couldn't find section for symbol %s", sym->name);
goto err;
}
if (GELF_ST_TYPE(sym->sym.st_info) == STT_SECTION) {
@ -536,8 +534,7 @@ static int read_symbols(struct elf *elf)
pnamelen = coldstr - sym->name;
pname = strndup(sym->name, pnamelen);
if (!pname) {
WARN("%s(): failed to allocate memory",
sym->name);
ERROR("%s(): failed to allocate memory", sym->name);
return -1;
}
@ -545,8 +542,7 @@ static int read_symbols(struct elf *elf)
free(pname);
if (!pfunc) {
WARN("%s(): can't find parent function",
sym->name);
ERROR("%s(): can't find parent function", sym->name);
return -1;
}
@ -583,7 +579,7 @@ static int elf_update_sym_relocs(struct elf *elf, struct symbol *sym)
{
struct reloc *reloc;
for (reloc = sym->relocs; reloc; reloc = reloc->sym_next_reloc)
for (reloc = sym->relocs; reloc; reloc = sym_next_reloc(reloc))
set_reloc_sym(elf, reloc, reloc->sym->idx);
return 0;
@ -613,14 +609,14 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,
s = elf_getscn(elf->elf, symtab->idx);
if (!s) {
WARN_ELF("elf_getscn");
ERROR_ELF("elf_getscn");
return -1;
}
if (symtab_shndx) {
t = elf_getscn(elf->elf, symtab_shndx->idx);
if (!t) {
WARN_ELF("elf_getscn");
ERROR_ELF("elf_getscn");
return -1;
}
}
@ -643,7 +639,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,
if (idx) {
/* we don't do holes in symbol tables */
WARN("index out of range");
ERROR("index out of range");
return -1;
}
@ -654,7 +650,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,
buf = calloc(num, entsize);
if (!buf) {
WARN("malloc");
ERROR_GLIBC("calloc");
return -1;
}
@ -669,7 +665,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,
if (t) {
buf = calloc(num, sizeof(Elf32_Word));
if (!buf) {
WARN("malloc");
ERROR_GLIBC("calloc");
return -1;
}
@ -687,7 +683,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,
/* empty blocks should not happen */
if (!symtab_data->d_size) {
WARN("zero size data");
ERROR("zero size data");
return -1;
}
@ -702,7 +698,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,
/* something went side-ways */
if (idx < 0) {
WARN("negative index");
ERROR("negative index");
return -1;
}
@ -714,13 +710,13 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,
} else {
sym->sym.st_shndx = SHN_XINDEX;
if (!shndx_data) {
WARN("no .symtab_shndx");
ERROR("no .symtab_shndx");
return -1;
}
}
if (!gelf_update_symshndx(symtab_data, shndx_data, idx, &sym->sym, shndx)) {
WARN_ELF("gelf_update_symshndx");
ERROR_ELF("gelf_update_symshndx");
return -1;
}
@ -738,7 +734,7 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym)
if (symtab) {
symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
} else {
WARN("no .symtab");
ERROR("no .symtab");
return NULL;
}
@ -760,7 +756,7 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym)
old->idx = new_idx;
if (elf_update_symbol(elf, symtab, symtab_shndx, old)) {
WARN("elf_update_symbol move");
ERROR("elf_update_symbol move");
return NULL;
}
@ -778,7 +774,7 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym)
non_local:
sym->idx = new_idx;
if (elf_update_symbol(elf, symtab, symtab_shndx, sym)) {
WARN("elf_update_symbol");
ERROR("elf_update_symbol");
return NULL;
}
@ -799,7 +795,7 @@ elf_create_section_symbol(struct elf *elf, struct section *sec)
struct symbol *sym = calloc(1, sizeof(*sym));
if (!sym) {
perror("malloc");
ERROR_GLIBC("malloc");
return NULL;
}
@ -829,7 +825,7 @@ elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size)
char *name = malloc(namelen);
if (!sym || !name) {
perror("malloc");
ERROR_GLIBC("malloc");
return NULL;
}
@ -858,16 +854,16 @@ static struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
struct reloc *reloc, empty = { 0 };
if (reloc_idx >= sec_num_entries(rsec)) {
WARN("%s: bad reloc_idx %u for %s with %d relocs",
__func__, reloc_idx, rsec->name, sec_num_entries(rsec));
ERROR("%s: bad reloc_idx %u for %s with %d relocs",
__func__, reloc_idx, rsec->name, sec_num_entries(rsec));
return NULL;
}
reloc = &rsec->relocs[reloc_idx];
if (memcmp(reloc, &empty, sizeof(empty))) {
WARN("%s: %s: reloc %d already initialized!",
__func__, rsec->name, reloc_idx);
ERROR("%s: %s: reloc %d already initialized!",
__func__, rsec->name, reloc_idx);
return NULL;
}
@ -880,7 +876,7 @@ static struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
set_reloc_addend(elf, reloc, addend);
elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc));
reloc->sym_next_reloc = sym->relocs;
set_sym_next_reloc(reloc, sym->relocs);
sym->relocs = reloc;
return reloc;
@ -896,8 +892,7 @@ struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
int addend = insn_off;
if (!(insn_sec->sh.sh_flags & SHF_EXECINSTR)) {
WARN("bad call to %s() for data symbol %s",
__func__, sym->name);
ERROR("bad call to %s() for data symbol %s", __func__, sym->name);
return NULL;
}
@ -926,8 +921,7 @@ struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec,
s64 addend)
{
if (sym->sec && (sec->sh.sh_flags & SHF_EXECINSTR)) {
WARN("bad call to %s() for text symbol %s",
__func__, sym->name);
ERROR("bad call to %s() for text symbol %s", __func__, sym->name);
return NULL;
}
@ -953,8 +947,7 @@ static int read_relocs(struct elf *elf)
rsec->base = find_section_by_index(elf, rsec->sh.sh_info);
if (!rsec->base) {
WARN("can't find base section for reloc section %s",
rsec->name);
ERROR("can't find base section for reloc section %s", rsec->name);
return -1;
}
@ -963,7 +956,7 @@ static int read_relocs(struct elf *elf)
nr_reloc = 0;
rsec->relocs = calloc(sec_num_entries(rsec), sizeof(*reloc));
if (!rsec->relocs) {
perror("calloc");
ERROR_GLIBC("calloc");
return -1;
}
for (i = 0; i < sec_num_entries(rsec); i++) {
@ -973,13 +966,12 @@ static int read_relocs(struct elf *elf)
symndx = reloc_sym(reloc);
reloc->sym = sym = find_symbol_by_index(elf, symndx);
if (!reloc->sym) {
WARN("can't find reloc entry symbol %d for %s",
symndx, rsec->name);
ERROR("can't find reloc entry symbol %d for %s", symndx, rsec->name);
return -1;
}
elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc));
reloc->sym_next_reloc = sym->relocs;
set_sym_next_reloc(reloc, sym->relocs);
sym->relocs = reloc;
nr_reloc++;
@ -1005,7 +997,7 @@ struct elf *elf_open_read(const char *name, int flags)
elf = malloc(sizeof(*elf));
if (!elf) {
perror("malloc");
ERROR_GLIBC("malloc");
return NULL;
}
memset(elf, 0, sizeof(*elf));
@ -1028,12 +1020,12 @@ struct elf *elf_open_read(const char *name, int flags)
elf->elf = elf_begin(elf->fd, cmd, NULL);
if (!elf->elf) {
WARN_ELF("elf_begin");
ERROR_ELF("elf_begin");
goto err;
}
if (!gelf_getehdr(elf->elf, &elf->ehdr)) {
WARN_ELF("gelf_getehdr");
ERROR_ELF("gelf_getehdr");
goto err;
}
@ -1062,19 +1054,19 @@ static int elf_add_string(struct elf *elf, struct section *strtab, char *str)
if (!strtab)
strtab = find_section_by_name(elf, ".strtab");
if (!strtab) {
WARN("can't find .strtab section");
ERROR("can't find .strtab section");
return -1;
}
s = elf_getscn(elf->elf, strtab->idx);
if (!s) {
WARN_ELF("elf_getscn");
ERROR_ELF("elf_getscn");
return -1;
}
data = elf_newdata(s);
if (!data) {
WARN_ELF("elf_newdata");
ERROR_ELF("elf_newdata");
return -1;
}
@ -1099,7 +1091,7 @@ struct section *elf_create_section(struct elf *elf, const char *name,
sec = malloc(sizeof(*sec));
if (!sec) {
perror("malloc");
ERROR_GLIBC("malloc");
return NULL;
}
memset(sec, 0, sizeof(*sec));
@ -1108,13 +1100,13 @@ struct section *elf_create_section(struct elf *elf, const char *name,
s = elf_newscn(elf->elf);
if (!s) {
WARN_ELF("elf_newscn");
ERROR_ELF("elf_newscn");
return NULL;
}
sec->name = strdup(name);
if (!sec->name) {
perror("strdup");
ERROR_GLIBC("strdup");
return NULL;
}
@ -1122,7 +1114,7 @@ struct section *elf_create_section(struct elf *elf, const char *name,
sec->data = elf_newdata(s);
if (!sec->data) {
WARN_ELF("elf_newdata");
ERROR_ELF("elf_newdata");
return NULL;
}
@ -1132,14 +1124,14 @@ struct section *elf_create_section(struct elf *elf, const char *name,
if (size) {
sec->data->d_buf = malloc(size);
if (!sec->data->d_buf) {
perror("malloc");
ERROR_GLIBC("malloc");
return NULL;
}
memset(sec->data->d_buf, 0, size);
}
if (!gelf_getshdr(s, &sec->sh)) {
WARN_ELF("gelf_getshdr");
ERROR_ELF("gelf_getshdr");
return NULL;
}
@ -1154,7 +1146,7 @@ struct section *elf_create_section(struct elf *elf, const char *name,
if (!shstrtab)
shstrtab = find_section_by_name(elf, ".strtab");
if (!shstrtab) {
WARN("can't find .shstrtab or .strtab section");
ERROR("can't find .shstrtab or .strtab section");
return NULL;
}
sec->sh.sh_name = elf_add_string(elf, shstrtab, sec->name);
@ -1179,7 +1171,7 @@ static struct section *elf_create_rela_section(struct elf *elf,
rsec_name = malloc(strlen(sec->name) + strlen(".rela") + 1);
if (!rsec_name) {
perror("malloc");
ERROR_GLIBC("malloc");
return NULL;
}
strcpy(rsec_name, ".rela");
@ -1199,7 +1191,7 @@ static struct section *elf_create_rela_section(struct elf *elf,
rsec->relocs = calloc(sec_num_entries(rsec), sizeof(struct reloc));
if (!rsec->relocs) {
perror("calloc");
ERROR_GLIBC("calloc");
return NULL;
}
@ -1232,7 +1224,7 @@ int elf_write_insn(struct elf *elf, struct section *sec,
Elf_Data *data = sec->data;
if (data->d_type != ELF_T_BYTE || data->d_off) {
WARN("write to unexpected data for section: %s", sec->name);
ERROR("write to unexpected data for section: %s", sec->name);
return -1;
}
@ -1261,7 +1253,7 @@ static int elf_truncate_section(struct elf *elf, struct section *sec)
s = elf_getscn(elf->elf, sec->idx);
if (!s) {
WARN_ELF("elf_getscn");
ERROR_ELF("elf_getscn");
return -1;
}
@ -1271,7 +1263,7 @@ static int elf_truncate_section(struct elf *elf, struct section *sec)
if (!data) {
if (size) {
WARN("end of section data but non-zero size left\n");
ERROR("end of section data but non-zero size left\n");
return -1;
}
return 0;
@ -1279,12 +1271,12 @@ static int elf_truncate_section(struct elf *elf, struct section *sec)
if (truncated) {
/* when we remove symbols */
WARN("truncated; but more data\n");
ERROR("truncated; but more data\n");
return -1;
}
if (!data->d_size) {
WARN("zero size data");
ERROR("zero size data");
return -1;
}
@ -1310,13 +1302,13 @@ int elf_write(struct elf *elf)
if (sec_changed(sec)) {
s = elf_getscn(elf->elf, sec->idx);
if (!s) {
WARN_ELF("elf_getscn");
ERROR_ELF("elf_getscn");
return -1;
}
/* Note this also flags the section dirty */
if (!gelf_update_shdr(s, &sec->sh)) {
WARN_ELF("gelf_update_shdr");
ERROR_ELF("gelf_update_shdr");
return -1;
}
@ -1329,7 +1321,7 @@ int elf_write(struct elf *elf)
/* Write all changes to the file. */
if (elf_update(elf->elf, ELF_C_WRITE) < 0) {
WARN_ELF("elf_update");
ERROR_ELF("elf_update");
return -1;
}

View file

@ -43,8 +43,10 @@ struct opts {
extern struct opts opts;
extern int cmd_parse_options(int argc, const char **argv, const char * const usage[]);
int cmd_parse_options(int argc, const char **argv, const char * const usage[]);
extern int objtool_run(int argc, const char **argv);
int objtool_run(int argc, const char **argv);
void print_args(void);
#endif /* _BUILTIN_H */

View file

@ -34,6 +34,8 @@ struct alt_group {
* This is shared with the other alt_groups in the same alternative.
*/
struct cfi_state **cfi;
bool ignore;
};
#define INSN_CHUNK_BITS 8
@ -54,7 +56,6 @@ struct instruction {
u32 idx : INSN_CHUNK_BITS,
dead_end : 1,
ignore : 1,
ignore_alts : 1,
hint : 1,
save : 1,

View file

@ -65,10 +65,11 @@ 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;
u8 ignore : 1;
struct list_head pv_target;
struct reloc *relocs;
};
@ -77,7 +78,7 @@ struct reloc {
struct elf_hash_node hash;
struct section *sec;
struct symbol *sym;
struct reloc *sym_next_reloc;
unsigned long _sym_next_reloc;
};
struct elf {
@ -297,6 +298,31 @@ static inline void set_reloc_type(struct elf *elf, struct reloc *reloc, unsigned
mark_sec_changed(elf, reloc->sec, true);
}
#define RELOC_JUMP_TABLE_BIT 1UL
/* Does reloc mark the beginning of a jump table? */
static inline bool is_jump_table(struct reloc *reloc)
{
return reloc->_sym_next_reloc & RELOC_JUMP_TABLE_BIT;
}
static inline void set_jump_table(struct reloc *reloc)
{
reloc->_sym_next_reloc |= RELOC_JUMP_TABLE_BIT;
}
static inline struct reloc *sym_next_reloc(struct reloc *reloc)
{
return (struct reloc *)(reloc->_sym_next_reloc & ~RELOC_JUMP_TABLE_BIT);
}
static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next)
{
unsigned long bit = reloc->_sym_next_reloc & RELOC_JUMP_TABLE_BIT;
reloc->_sym_next_reloc = (unsigned long)next | bit;
}
#define for_each_sec(file, sec) \
list_for_each_entry(sec, &file->elf->sections, list)

View file

@ -41,7 +41,7 @@ struct objtool_file {
struct objtool_file *objtool_open_read(const char *_objname);
void objtool_pv_add(struct objtool_file *file, int idx, struct symbol *func);
int objtool_pv_add(struct objtool_file *file, int idx, struct symbol *func);
int check(struct objtool_file *file);
int orc_dump(const char *objname);

View file

@ -16,8 +16,6 @@ struct special_alt {
struct list_head list;
bool group;
bool skip_orig;
bool skip_alt;
bool jump_or_nop;
u8 key_addend;
@ -32,7 +30,7 @@ struct special_alt {
int special_get_alts(struct elf *elf, struct list_head *alts);
void arch_handle_alternative(unsigned short feature, struct special_alt *alt);
void arch_handle_alternative(struct special_alt *alt);
bool arch_support_alt_relocation(struct special_alt *special_alt,
struct instruction *insn,

View file

@ -11,6 +11,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <objtool/builtin.h>
#include <objtool/elf.h>
@ -41,36 +42,46 @@ static inline char *offstr(struct section *sec, unsigned long offset)
return str;
}
#define WARN(format, ...) \
fprintf(stderr, \
"%s: %s: objtool: " format "\n", \
objname, \
opts.werror ? "error" : "warning", \
#define ___WARN(severity, extra, format, ...) \
fprintf(stderr, \
"%s%s%s: objtool" extra ": " format "\n", \
objname ?: "", \
objname ? ": " : "", \
severity, \
##__VA_ARGS__)
#define WARN_FUNC(format, sec, offset, ...) \
({ \
char *_str = offstr(sec, offset); \
WARN("%s: " format, _str, ##__VA_ARGS__); \
free(_str); \
#define __WARN(severity, format, ...) \
___WARN(severity, "", format, ##__VA_ARGS__)
#define __WARN_LINE(severity, format, ...) \
___WARN(severity, " [%s:%d]", format, __FILE__, __LINE__, ##__VA_ARGS__)
#define __WARN_ELF(severity, format, ...) \
__WARN_LINE(severity, "%s: " format " failed: %s", __func__, ##__VA_ARGS__, elf_errmsg(-1))
#define __WARN_GLIBC(severity, format, ...) \
__WARN_LINE(severity, "%s: " format " failed: %s", __func__, ##__VA_ARGS__, strerror(errno))
#define __WARN_FUNC(severity, sec, offset, format, ...) \
({ \
char *_str = offstr(sec, offset); \
__WARN(severity, "%s: " format, _str, ##__VA_ARGS__); \
free(_str); \
})
#define WARN_LIMIT 2
#define WARN_STR (opts.werror ? "error" : "warning")
#define WARN(format, ...) __WARN(WARN_STR, format, ##__VA_ARGS__)
#define WARN_FUNC(sec, offset, format, ...) __WARN_FUNC(WARN_STR, sec, offset, format, ##__VA_ARGS__)
#define WARN_INSN(insn, format, ...) \
({ \
struct instruction *_insn = (insn); \
BUILD_BUG_ON(WARN_LIMIT > 2); \
if (!_insn->sym || _insn->sym->warnings < WARN_LIMIT) { \
WARN_FUNC(format, _insn->sec, _insn->offset, \
if (!_insn->sym || !_insn->sym->warned) \
WARN_FUNC(_insn->sec, _insn->offset, format, \
##__VA_ARGS__); \
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++; \
} \
if (_insn->sym) \
_insn->sym->warned = 1; \
})
#define BT_INSN(insn, format, ...) \
@ -83,7 +94,12 @@ static inline char *offstr(struct section *sec, unsigned long offset)
} \
})
#define WARN_ELF(format, ...) \
WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1))
#define ERROR_STR "error"
#define ERROR(format, ...) __WARN(ERROR_STR, format, ##__VA_ARGS__)
#define ERROR_ELF(format, ...) __WARN_ELF(ERROR_STR, format, ##__VA_ARGS__)
#define ERROR_GLIBC(format, ...) __WARN_GLIBC(ERROR_STR, format, ##__VA_ARGS__)
#define ERROR_FUNC(sec, offset, format, ...) __WARN_FUNC(ERROR_STR, sec, offset, format, ##__VA_ARGS__)
#define ERROR_INSN(insn, format, ...) WARN_FUNC(insn->sec, insn->offset, format, ##__VA_ARGS__)
#endif /* _WARN_H */

View file

@ -23,7 +23,7 @@ static struct objtool_file file;
struct objtool_file *objtool_open_read(const char *filename)
{
if (file.elf) {
WARN("won't handle more than one file at a time");
ERROR("won't handle more than one file at a time");
return NULL;
}
@ -44,14 +44,14 @@ struct objtool_file *objtool_open_read(const char *filename)
return &file;
}
void objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func)
int objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func)
{
if (!opts.noinstr)
return;
return 0;
if (!f->pv_ops) {
WARN("paravirt confusion");
return;
ERROR("paravirt confusion");
return -1;
}
/*
@ -60,14 +60,15 @@ void objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func)
*/
if (!strcmp(func->name, "_paravirt_nop") ||
!strcmp(func->name, "_paravirt_ident_64"))
return;
return 0;
/* already added this function */
if (!list_empty(&func->pv_target))
return;
return 0;
list_add(&func->pv_target, &f->pv_ops[idx].targets);
f->pv_ops[idx].clean = false;
return 0;
}
int main(int argc, const char **argv)

View file

@ -36,47 +36,47 @@ int orc_dump(const char *filename)
elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
if (!elf) {
WARN_ELF("elf_begin");
ERROR_ELF("elf_begin");
return -1;
}
if (!elf64_getehdr(elf)) {
WARN_ELF("elf64_getehdr");
ERROR_ELF("elf64_getehdr");
return -1;
}
memcpy(&dummy_elf.ehdr, elf64_getehdr(elf), sizeof(dummy_elf.ehdr));
if (elf_getshdrnum(elf, &nr_sections)) {
WARN_ELF("elf_getshdrnum");
ERROR_ELF("elf_getshdrnum");
return -1;
}
if (elf_getshdrstrndx(elf, &shstrtab_idx)) {
WARN_ELF("elf_getshdrstrndx");
ERROR_ELF("elf_getshdrstrndx");
return -1;
}
for (i = 0; i < nr_sections; i++) {
scn = elf_getscn(elf, i);
if (!scn) {
WARN_ELF("elf_getscn");
ERROR_ELF("elf_getscn");
return -1;
}
if (!gelf_getshdr(scn, &sh)) {
WARN_ELF("gelf_getshdr");
ERROR_ELF("gelf_getshdr");
return -1;
}
name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
if (!name) {
WARN_ELF("elf_strptr");
ERROR_ELF("elf_strptr");
return -1;
}
data = elf_getdata(scn, NULL);
if (!data) {
WARN_ELF("elf_getdata");
ERROR_ELF("elf_getdata");
return -1;
}
@ -99,7 +99,7 @@ int orc_dump(const char *filename)
return 0;
if (orc_size % sizeof(*orc) != 0) {
WARN("bad .orc_unwind section size");
ERROR("bad .orc_unwind section size");
return -1;
}
@ -107,36 +107,36 @@ int orc_dump(const char *filename)
for (i = 0; i < nr_entries; i++) {
if (rela_orc_ip) {
if (!gelf_getrela(rela_orc_ip, i, &rela)) {
WARN_ELF("gelf_getrela");
ERROR_ELF("gelf_getrela");
return -1;
}
if (!gelf_getsym(symtab, GELF_R_SYM(rela.r_info), &sym)) {
WARN_ELF("gelf_getsym");
ERROR_ELF("gelf_getsym");
return -1;
}
if (GELF_ST_TYPE(sym.st_info) == STT_SECTION) {
scn = elf_getscn(elf, sym.st_shndx);
if (!scn) {
WARN_ELF("elf_getscn");
ERROR_ELF("elf_getscn");
return -1;
}
if (!gelf_getshdr(scn, &sh)) {
WARN_ELF("gelf_getshdr");
ERROR_ELF("gelf_getshdr");
return -1;
}
name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
if (!name) {
WARN_ELF("elf_strptr");
ERROR_ELF("elf_strptr");
return -1;
}
} else {
name = elf_strptr(elf, strtab_idx, sym.st_name);
if (!name) {
WARN_ELF("elf_strptr");
ERROR_ELF("elf_strptr");
return -1;
}
}

View file

@ -54,7 +54,7 @@ static const struct special_entry entries[] = {
{},
};
void __weak arch_handle_alternative(unsigned short feature, struct special_alt *alt)
void __weak arch_handle_alternative(struct special_alt *alt)
{
}
@ -86,27 +86,18 @@ static int get_alt_entry(struct elf *elf, const struct special_entry *entry,
orig_reloc = find_reloc_by_dest(elf, sec, offset + entry->orig);
if (!orig_reloc) {
WARN_FUNC("can't find orig reloc", sec, offset + entry->orig);
ERROR_FUNC(sec, offset + entry->orig, "can't find orig reloc");
return -1;
}
reloc_to_sec_off(orig_reloc, &alt->orig_sec, &alt->orig_off);
if (entry->feature) {
unsigned short feature;
feature = bswap_if_needed(elf,
*(unsigned short *)(sec->data->d_buf +
offset +
entry->feature));
arch_handle_alternative(feature, alt);
}
arch_handle_alternative(alt);
if (!entry->group || alt->new_len) {
new_reloc = find_reloc_by_dest(elf, sec, offset + entry->new);
if (!new_reloc) {
WARN_FUNC("can't find new reloc",
sec, offset + entry->new);
ERROR_FUNC(sec, offset + entry->new, "can't find new reloc");
return -1;
}
@ -122,8 +113,7 @@ static int get_alt_entry(struct elf *elf, const struct special_entry *entry,
key_reloc = find_reloc_by_dest(elf, sec, offset + entry->key);
if (!key_reloc) {
WARN_FUNC("can't find key reloc",
sec, offset + entry->key);
ERROR_FUNC(sec, offset + entry->key, "can't find key reloc");
return -1;
}
alt->key_addend = reloc_addend(key_reloc);
@ -153,8 +143,7 @@ int special_get_alts(struct elf *elf, struct list_head *alts)
continue;
if (sec->sh.sh_size % entry->size != 0) {
WARN("%s size not a multiple of %d",
sec->name, entry->size);
ERROR("%s size not a multiple of %d", sec->name, entry->size);
return -1;
}
@ -163,7 +152,7 @@ int special_get_alts(struct elf *elf, struct list_head *alts)
for (idx = 0; idx < nr_entries; idx++) {
alt = malloc(sizeof(*alt));
if (!alt) {
WARN("malloc failed");
ERROR_GLIBC("malloc failed");
return -1;
}
memset(alt, 0, sizeof(*alt));