Changes in this cycle were:
- Shrink 'struct instruction', to improve objtool performance & memory footprint. - Other maximum memory usage reductions - this makes the build both faster, and fixes kernel build OOM failures on allyesconfig and similar configs when they try to build the final (large) vmlinux.o. - Fix ORC unwinding when a kprobe (INT3) is set on a stack-modifying single-byte instruction (PUSH/POP or LEAVE). This requires the extension of the ORC metadata structure with a 'signal' field. - Misc fixes & cleanups. Signed-off-by: Ingo Molnar <mingo@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAmQAVp8RHG1pbmdvQGtl cm5lbC5vcmcACgkQEnMQ0APhK1gV6A//YbWb4nNxYbRFBd1O3FnFfy4efrDQ4btI hwkL6f7jka9RnIpIEatJvaLdNvyN5tuPCC/+B5eVnvFdd1JcBUmj5D+zYFt6H6qt BG4M6TNHFkP1kOJVfFGn8UPRfoMz2oMiEqilpsc1Yuf7b3ldMJtGUoHaeZC9pyqe RUisKNw4WHZp2G/gTBUWxW17xpWY3Awgch/w4HCu8wMnR+uEC44i0UCBfnAadl36 ar66PfhMJcQIv0XkK9wu43g7+HFnjpxHOx35JW3lRot0xRnwl/JcsmaX5iPkh0gt HV8eLH80J0homeMZDY7vWIKJxGeLkIdfjO5gxwTdnFc9rQw3GwHp1B7WTS6J3Vwe gM00kyaGly3CvkKMiz5QQBfViWCjE25nYS8X0i9Oz6Gk58IkRPGByaDTKRjNrDJB BwH9DE9xb3dPVZRv/PejkTdggQWo+FDTrL8ulHIjUFK11M7VubwkskecNHkfpAOE TRy5iLjMocF8u7hdyec6Mma2K6qEndC2Rw9ZMPQ7TeieMsBcl63cSRgSJLFfdRhr /5c6Hr2SNQKU8xu+3j49GyBwFvp4CwCa+GPs9/o+l0uCvuKNIn9B788cm4TjxLJ9 C3PRzE6B/CaLhYvlC5k5cNM+I4YpoMU/mvSvY6HcC0Duj2nSAWS2VV60MVMDpqVX 8nK4xnla2tM= =bpPY -----END PGP SIGNATURE----- Merge tag 'objtool-core-2023-03-02' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull objtool updates from Ingo Molnar: - Shrink 'struct instruction', to improve objtool performance & memory footprint - Other maximum memory usage reductions - this makes the build both faster, and fixes kernel build OOM failures on allyesconfig and similar configs when they try to build the final (large) vmlinux.o - Fix ORC unwinding when a kprobe (INT3) is set on a stack-modifying single-byte instruction (PUSH/POP or LEAVE). This requires the extension of the ORC metadata structure with a 'signal' field - Misc fixes & cleanups * tag 'objtool-core-2023-03-02' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (22 commits) objtool: Fix ORC 'signal' propagation objtool: Remove instruction::list x86: Fix FILL_RETURN_BUFFER objtool: Fix overlapping alternatives objtool: Union instruction::{call_dest,jump_table} objtool: Remove instruction::reloc objtool: Shrink instruction::{type,visited} objtool: Make instruction::alts a single-linked list objtool: Make instruction::stack_ops a single-linked list objtool: Change arch_decode_instruction() signature x86/entry: Fix unwinding from kprobe on PUSH/POP instruction x86/unwind/orc: Add 'signal' field to ORC metadata objtool: Optimize layout of struct special_alt objtool: Optimize layout of struct symbol objtool: Allocate multiple structures with calloc() objtool: Make struct check_options static objtool: Make struct entries[] static and const objtool: Fix HOSTCC flag usage objtool: Properly support make V=1 objtool: Install libsubcmd in build ...
This commit is contained in:
commit
857f1268a5
|
@ -385,7 +385,14 @@ SYM_CODE_END(xen_error_entry)
|
|||
*/
|
||||
.macro idtentry vector asmsym cfunc has_error_code:req
|
||||
SYM_CODE_START(\asmsym)
|
||||
UNWIND_HINT_IRET_REGS offset=\has_error_code*8
|
||||
|
||||
.if \vector == X86_TRAP_BP
|
||||
/* #BP advances %rip to the next instruction */
|
||||
UNWIND_HINT_IRET_REGS offset=\has_error_code*8 signal=0
|
||||
.else
|
||||
UNWIND_HINT_IRET_REGS offset=\has_error_code*8
|
||||
.endif
|
||||
|
||||
ENDBR
|
||||
ASM_CLAC
|
||||
cld
|
||||
|
|
|
@ -261,7 +261,7 @@
|
|||
.macro FILL_RETURN_BUFFER reg:req nr:req ftr:req ftr2=ALT_NOT(X86_FEATURE_ALWAYS)
|
||||
ALTERNATIVE_2 "jmp .Lskip_rsb_\@", \
|
||||
__stringify(__FILL_RETURN_BUFFER(\reg,\nr)), \ftr, \
|
||||
__stringify(__FILL_ONE_RETURN), \ftr2
|
||||
__stringify(nop;nop;__FILL_ONE_RETURN), \ftr2
|
||||
|
||||
.Lskip_rsb_\@:
|
||||
.endm
|
||||
|
|
|
@ -57,12 +57,14 @@ struct orc_entry {
|
|||
unsigned sp_reg:4;
|
||||
unsigned bp_reg:4;
|
||||
unsigned type:2;
|
||||
unsigned signal:1;
|
||||
unsigned end:1;
|
||||
#elif defined(__BIG_ENDIAN_BITFIELD)
|
||||
unsigned bp_reg:4;
|
||||
unsigned sp_reg:4;
|
||||
unsigned unused:5;
|
||||
unsigned unused:4;
|
||||
unsigned end:1;
|
||||
unsigned signal:1;
|
||||
unsigned type:2;
|
||||
#endif
|
||||
} __packed;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
UNWIND_HINT type=UNWIND_HINT_TYPE_ENTRY end=1
|
||||
.endm
|
||||
|
||||
.macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 partial=0
|
||||
.macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 partial=0 signal=1
|
||||
.if \base == %rsp
|
||||
.if \indirect
|
||||
.set sp_reg, ORC_REG_SP_INDIRECT
|
||||
|
@ -45,11 +45,11 @@
|
|||
.set type, UNWIND_HINT_TYPE_REGS
|
||||
.endif
|
||||
|
||||
UNWIND_HINT sp_reg=sp_reg sp_offset=sp_offset type=type
|
||||
UNWIND_HINT sp_reg=sp_reg sp_offset=sp_offset type=type signal=\signal
|
||||
.endm
|
||||
|
||||
.macro UNWIND_HINT_IRET_REGS base=%rsp offset=0
|
||||
UNWIND_HINT_REGS base=\base offset=\offset partial=1
|
||||
.macro UNWIND_HINT_IRET_REGS base=%rsp offset=0 signal=1
|
||||
UNWIND_HINT_REGS base=\base offset=\offset partial=1 signal=\signal
|
||||
.endm
|
||||
|
||||
.macro UNWIND_HINT_FUNC
|
||||
|
@ -67,7 +67,7 @@
|
|||
#else
|
||||
|
||||
#define UNWIND_HINT_FUNC \
|
||||
UNWIND_HINT(ORC_REG_SP, 8, UNWIND_HINT_TYPE_FUNC, 0)
|
||||
UNWIND_HINT(ORC_REG_SP, 8, UNWIND_HINT_TYPE_FUNC, 0, 0)
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
|
|
|
@ -484,6 +484,8 @@ bool unwind_next_frame(struct unwind_state *state)
|
|||
goto the_end;
|
||||
}
|
||||
|
||||
state->signal = orc->signal;
|
||||
|
||||
/* Find the previous frame's stack: */
|
||||
switch (orc->sp_reg) {
|
||||
case ORC_REG_SP:
|
||||
|
@ -563,7 +565,6 @@ bool unwind_next_frame(struct unwind_state *state)
|
|||
state->sp = sp;
|
||||
state->regs = NULL;
|
||||
state->prev_regs = NULL;
|
||||
state->signal = false;
|
||||
break;
|
||||
|
||||
case UNWIND_HINT_TYPE_REGS:
|
||||
|
@ -587,7 +588,6 @@ bool unwind_next_frame(struct unwind_state *state)
|
|||
state->regs = (struct pt_regs *)sp;
|
||||
state->prev_regs = NULL;
|
||||
state->full_regs = true;
|
||||
state->signal = true;
|
||||
break;
|
||||
|
||||
case UNWIND_HINT_TYPE_REGS_PARTIAL:
|
||||
|
@ -604,7 +604,6 @@ bool unwind_next_frame(struct unwind_state *state)
|
|||
state->prev_regs = state->regs;
|
||||
state->regs = (void *)sp - IRET_FRAME_OFFSET;
|
||||
state->full_regs = false;
|
||||
state->signal = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -15,6 +15,7 @@ struct unwind_hint {
|
|||
s16 sp_offset;
|
||||
u8 sp_reg;
|
||||
u8 type;
|
||||
u8 signal;
|
||||
u8 end;
|
||||
};
|
||||
#endif
|
||||
|
@ -49,7 +50,7 @@ struct unwind_hint {
|
|||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#define UNWIND_HINT(sp_reg, sp_offset, type, end) \
|
||||
#define UNWIND_HINT(sp_reg, sp_offset, type, signal, end) \
|
||||
"987: \n\t" \
|
||||
".pushsection .discard.unwind_hints\n\t" \
|
||||
/* struct unwind_hint */ \
|
||||
|
@ -57,6 +58,7 @@ struct unwind_hint {
|
|||
".short " __stringify(sp_offset) "\n\t" \
|
||||
".byte " __stringify(sp_reg) "\n\t" \
|
||||
".byte " __stringify(type) "\n\t" \
|
||||
".byte " __stringify(signal) "\n\t" \
|
||||
".byte " __stringify(end) "\n\t" \
|
||||
".balign 4 \n\t" \
|
||||
".popsection\n\t"
|
||||
|
@ -129,7 +131,7 @@ struct unwind_hint {
|
|||
* the debuginfo as necessary. It will also warn if it sees any
|
||||
* inconsistencies.
|
||||
*/
|
||||
.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 end=0
|
||||
.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 end=0
|
||||
.Lunwind_hint_ip_\@:
|
||||
.pushsection .discard.unwind_hints
|
||||
/* struct unwind_hint */
|
||||
|
@ -137,6 +139,7 @@ struct unwind_hint {
|
|||
.short \sp_offset
|
||||
.byte \sp_reg
|
||||
.byte \type
|
||||
.byte \signal
|
||||
.byte \end
|
||||
.balign 4
|
||||
.popsection
|
||||
|
@ -174,7 +177,7 @@ struct unwind_hint {
|
|||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#define UNWIND_HINT(sp_reg, sp_offset, type, end) \
|
||||
#define UNWIND_HINT(sp_reg, sp_offset, type, signal, end) \
|
||||
"\n\t"
|
||||
#define STACK_FRAME_NON_STANDARD(func)
|
||||
#define STACK_FRAME_NON_STANDARD_FP(func)
|
||||
|
@ -182,7 +185,7 @@ struct unwind_hint {
|
|||
#define ASM_REACHABLE
|
||||
#else
|
||||
#define ANNOTATE_INTRA_FUNCTION_CALL
|
||||
.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 end=0
|
||||
.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 end=0
|
||||
.endm
|
||||
.macro STACK_FRAME_NON_STANDARD func:req
|
||||
.endm
|
||||
|
|
|
@ -57,12 +57,14 @@ struct orc_entry {
|
|||
unsigned sp_reg:4;
|
||||
unsigned bp_reg:4;
|
||||
unsigned type:2;
|
||||
unsigned signal:1;
|
||||
unsigned end:1;
|
||||
#elif defined(__BIG_ENDIAN_BITFIELD)
|
||||
unsigned bp_reg:4;
|
||||
unsigned sp_reg:4;
|
||||
unsigned unused:5;
|
||||
unsigned unused:4;
|
||||
unsigned end:1;
|
||||
unsigned signal:1;
|
||||
unsigned type:2;
|
||||
#endif
|
||||
} __packed;
|
||||
|
|
|
@ -15,6 +15,7 @@ struct unwind_hint {
|
|||
s16 sp_offset;
|
||||
u8 sp_reg;
|
||||
u8 type;
|
||||
u8 signal;
|
||||
u8 end;
|
||||
};
|
||||
#endif
|
||||
|
@ -49,7 +50,7 @@ struct unwind_hint {
|
|||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#define UNWIND_HINT(sp_reg, sp_offset, type, end) \
|
||||
#define UNWIND_HINT(sp_reg, sp_offset, type, signal, end) \
|
||||
"987: \n\t" \
|
||||
".pushsection .discard.unwind_hints\n\t" \
|
||||
/* struct unwind_hint */ \
|
||||
|
@ -57,6 +58,7 @@ struct unwind_hint {
|
|||
".short " __stringify(sp_offset) "\n\t" \
|
||||
".byte " __stringify(sp_reg) "\n\t" \
|
||||
".byte " __stringify(type) "\n\t" \
|
||||
".byte " __stringify(signal) "\n\t" \
|
||||
".byte " __stringify(end) "\n\t" \
|
||||
".balign 4 \n\t" \
|
||||
".popsection\n\t"
|
||||
|
@ -129,7 +131,7 @@ struct unwind_hint {
|
|||
* the debuginfo as necessary. It will also warn if it sees any
|
||||
* inconsistencies.
|
||||
*/
|
||||
.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 end=0
|
||||
.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 end=0
|
||||
.Lunwind_hint_ip_\@:
|
||||
.pushsection .discard.unwind_hints
|
||||
/* struct unwind_hint */
|
||||
|
@ -137,6 +139,7 @@ struct unwind_hint {
|
|||
.short \sp_offset
|
||||
.byte \sp_reg
|
||||
.byte \type
|
||||
.byte \signal
|
||||
.byte \end
|
||||
.balign 4
|
||||
.popsection
|
||||
|
@ -174,7 +177,7 @@ struct unwind_hint {
|
|||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#define UNWIND_HINT(sp_reg, sp_offset, type, end) \
|
||||
#define UNWIND_HINT(sp_reg, sp_offset, type, signal, end) \
|
||||
"\n\t"
|
||||
#define STACK_FRAME_NON_STANDARD(func)
|
||||
#define STACK_FRAME_NON_STANDARD_FP(func)
|
||||
|
@ -182,7 +185,7 @@ struct unwind_hint {
|
|||
#define ASM_REACHABLE
|
||||
#else
|
||||
#define ANNOTATE_INTRA_FUNCTION_CALL
|
||||
.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 end=0
|
||||
.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 end=0
|
||||
.endm
|
||||
.macro STACK_FRAME_NON_STANDARD func:req
|
||||
.endm
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
arch/x86/lib/inat-tables.c
|
||||
/objtool
|
||||
fixdep
|
||||
libsubcmd/
|
||||
|
|
|
@ -16,8 +16,6 @@ objtool-y += libctype.o
|
|||
objtool-y += str_error_r.o
|
||||
objtool-y += librbtree.o
|
||||
|
||||
CFLAGS += -I$(srctree)/tools/lib
|
||||
|
||||
$(OUTPUT)libstring.o: ../lib/string.c FORCE
|
||||
$(call rule_mkdir)
|
||||
$(call if_changed_dep,cc_o_c)
|
||||
|
|
|
@ -410,6 +410,14 @@ the objtool maintainers.
|
|||
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.
|
||||
|
||||
|
||||
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.
|
||||
|
|
|
@ -2,19 +2,18 @@
|
|||
include ../scripts/Makefile.include
|
||||
include ../scripts/Makefile.arch
|
||||
|
||||
# always use the host compiler
|
||||
AR = $(HOSTAR)
|
||||
CC = $(HOSTCC)
|
||||
LD = $(HOSTLD)
|
||||
|
||||
ifeq ($(srctree),)
|
||||
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
endif
|
||||
|
||||
SUBCMD_SRCDIR = $(srctree)/tools/lib/subcmd/
|
||||
LIBSUBCMD_OUTPUT = $(or $(OUTPUT),$(CURDIR)/)
|
||||
LIBSUBCMD = $(LIBSUBCMD_OUTPUT)libsubcmd.a
|
||||
LIBSUBCMD_DIR = $(srctree)/tools/lib/subcmd/
|
||||
ifneq ($(OUTPUT),)
|
||||
LIBSUBCMD_OUTPUT = $(abspath $(OUTPUT))/libsubcmd
|
||||
else
|
||||
LIBSUBCMD_OUTPUT = $(CURDIR)/libsubcmd
|
||||
endif
|
||||
LIBSUBCMD = $(LIBSUBCMD_OUTPUT)/libsubcmd.a
|
||||
|
||||
OBJTOOL := $(OUTPUT)objtool
|
||||
OBJTOOL_IN := $(OBJTOOL)-in.o
|
||||
|
@ -28,16 +27,29 @@ INCLUDES := -I$(srctree)/tools/include \
|
|||
-I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \
|
||||
-I$(srctree)/tools/arch/$(SRCARCH)/include \
|
||||
-I$(srctree)/tools/objtool/include \
|
||||
-I$(srctree)/tools/objtool/arch/$(SRCARCH)/include
|
||||
-I$(srctree)/tools/objtool/arch/$(SRCARCH)/include \
|
||||
-I$(LIBSUBCMD_OUTPUT)/include
|
||||
# Note, EXTRA_WARNINGS here was determined for CC and not HOSTCC, it
|
||||
# is passed here to match a legacy behavior.
|
||||
WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed -Wno-nested-externs
|
||||
CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
|
||||
LDFLAGS += $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS)
|
||||
OBJTOOL_CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
|
||||
OBJTOOL_LDFLAGS := $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS)
|
||||
|
||||
# Allow old libelf to be used:
|
||||
elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(CC) $(CFLAGS) -x c -E - | grep elf_getshdr)
|
||||
CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED)
|
||||
elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - | grep elf_getshdr)
|
||||
OBJTOOL_CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED)
|
||||
|
||||
# Always want host compilation.
|
||||
HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)"
|
||||
|
||||
AWK = awk
|
||||
MKDIR = mkdir
|
||||
|
||||
ifeq ($(V),1)
|
||||
Q =
|
||||
else
|
||||
Q = @
|
||||
endif
|
||||
|
||||
BUILD_ORC := n
|
||||
|
||||
|
@ -49,21 +61,33 @@ export BUILD_ORC
|
|||
export srctree OUTPUT CFLAGS SRCARCH AWK
|
||||
include $(srctree)/tools/build/Makefile.include
|
||||
|
||||
$(OBJTOOL_IN): fixdep FORCE
|
||||
@$(CONFIG_SHELL) ./sync-check.sh
|
||||
@$(MAKE) $(build)=objtool
|
||||
$(OBJTOOL_IN): fixdep $(LIBSUBCMD) FORCE
|
||||
$(Q)$(CONFIG_SHELL) ./sync-check.sh
|
||||
$(Q)$(MAKE) $(build)=objtool $(HOST_OVERRIDES) CFLAGS="$(OBJTOOL_CFLAGS)" \
|
||||
LDFLAGS="$(OBJTOOL_LDFLAGS)"
|
||||
|
||||
|
||||
$(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN)
|
||||
$(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@
|
||||
$(QUIET_LINK)$(HOSTCC) $(OBJTOOL_IN) $(OBJTOOL_LDFLAGS) -o $@
|
||||
|
||||
|
||||
$(LIBSUBCMD): fixdep FORCE
|
||||
$(Q)$(MAKE) -C $(SUBCMD_SRCDIR) OUTPUT=$(LIBSUBCMD_OUTPUT)
|
||||
$(LIBSUBCMD_OUTPUT):
|
||||
$(Q)$(MKDIR) -p $@
|
||||
|
||||
clean:
|
||||
$(LIBSUBCMD): fixdep $(LIBSUBCMD_OUTPUT) FORCE
|
||||
$(Q)$(MAKE) -C $(LIBSUBCMD_DIR) O=$(LIBSUBCMD_OUTPUT) \
|
||||
DESTDIR=$(LIBSUBCMD_OUTPUT) prefix= subdir= \
|
||||
$(HOST_OVERRIDES) EXTRA_CFLAGS="$(OBJTOOL_CFLAGS)" \
|
||||
$@ install_headers
|
||||
|
||||
$(LIBSUBCMD)-clean:
|
||||
$(call QUIET_CLEAN, libsubcmd)
|
||||
$(Q)$(RM) -r -- $(LIBSUBCMD_OUTPUT)
|
||||
|
||||
clean: $(LIBSUBCMD)-clean
|
||||
$(call QUIET_CLEAN, objtool) $(RM) $(OBJTOOL)
|
||||
$(Q)find $(OUTPUT) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
|
||||
$(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep $(LIBSUBCMD)
|
||||
$(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep
|
||||
|
||||
FORCE:
|
||||
|
||||
|
|
|
@ -41,38 +41,36 @@ const char *arch_ret_insn(int len)
|
|||
|
||||
int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
|
||||
unsigned long offset, unsigned int maxlen,
|
||||
unsigned int *len, enum insn_type *type,
|
||||
unsigned long *immediate,
|
||||
struct list_head *ops_list)
|
||||
struct instruction *insn)
|
||||
{
|
||||
unsigned int opcode;
|
||||
enum insn_type typ;
|
||||
unsigned long imm;
|
||||
u32 insn;
|
||||
u32 ins;
|
||||
|
||||
insn = bswap_if_needed(file->elf, *(u32 *)(sec->data->d_buf + offset));
|
||||
opcode = insn >> 26;
|
||||
ins = bswap_if_needed(file->elf, *(u32 *)(sec->data->d_buf + offset));
|
||||
opcode = ins >> 26;
|
||||
typ = INSN_OTHER;
|
||||
imm = 0;
|
||||
|
||||
switch (opcode) {
|
||||
case 18: /* b[l][a] */
|
||||
if ((insn & 3) == 1) /* bl */
|
||||
if ((ins & 3) == 1) /* bl */
|
||||
typ = INSN_CALL;
|
||||
|
||||
imm = insn & 0x3fffffc;
|
||||
imm = ins & 0x3fffffc;
|
||||
if (imm & 0x2000000)
|
||||
imm -= 0x4000000;
|
||||
break;
|
||||
}
|
||||
|
||||
if (opcode == 1)
|
||||
*len = 8;
|
||||
insn->len = 8;
|
||||
else
|
||||
*len = 4;
|
||||
insn->len = 4;
|
||||
|
||||
*type = typ;
|
||||
*immediate = imm;
|
||||
insn->type = typ;
|
||||
insn->immediate = imm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ bool arch_pc_relative_reloc(struct reloc *reloc)
|
|||
#define ADD_OP(op) \
|
||||
if (!(op = calloc(1, sizeof(*op)))) \
|
||||
return -1; \
|
||||
else for (list_add_tail(&op->list, ops_list); op; op = NULL)
|
||||
else for (*ops_list = op, ops_list = &op->next; op; op = NULL)
|
||||
|
||||
/*
|
||||
* Helpers to decode ModRM/SIB:
|
||||
|
@ -146,12 +146,11 @@ static bool has_notrack_prefix(struct insn *insn)
|
|||
|
||||
int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
|
||||
unsigned long offset, unsigned int maxlen,
|
||||
unsigned int *len, enum insn_type *type,
|
||||
unsigned long *immediate,
|
||||
struct list_head *ops_list)
|
||||
struct instruction *insn)
|
||||
{
|
||||
struct stack_op **ops_list = &insn->stack_ops;
|
||||
const struct elf *elf = file->elf;
|
||||
struct insn insn;
|
||||
struct insn ins;
|
||||
int x86_64, ret;
|
||||
unsigned char op1, op2, op3, prefix,
|
||||
rex = 0, rex_b = 0, rex_r = 0, rex_w = 0, rex_x = 0,
|
||||
|
@ -165,42 +164,42 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
if (x86_64 == -1)
|
||||
return -1;
|
||||
|
||||
ret = insn_decode(&insn, sec->data->d_buf + offset, maxlen,
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*len = insn.length;
|
||||
*type = INSN_OTHER;
|
||||
insn->len = ins.length;
|
||||
insn->type = INSN_OTHER;
|
||||
|
||||
if (insn.vex_prefix.nbytes)
|
||||
if (ins.vex_prefix.nbytes)
|
||||
return 0;
|
||||
|
||||
prefix = insn.prefixes.bytes[0];
|
||||
prefix = ins.prefixes.bytes[0];
|
||||
|
||||
op1 = insn.opcode.bytes[0];
|
||||
op2 = insn.opcode.bytes[1];
|
||||
op3 = insn.opcode.bytes[2];
|
||||
op1 = ins.opcode.bytes[0];
|
||||
op2 = ins.opcode.bytes[1];
|
||||
op3 = ins.opcode.bytes[2];
|
||||
|
||||
if (insn.rex_prefix.nbytes) {
|
||||
rex = insn.rex_prefix.bytes[0];
|
||||
if (ins.rex_prefix.nbytes) {
|
||||
rex = ins.rex_prefix.bytes[0];
|
||||
rex_w = X86_REX_W(rex) >> 3;
|
||||
rex_r = X86_REX_R(rex) >> 2;
|
||||
rex_x = X86_REX_X(rex) >> 1;
|
||||
rex_b = X86_REX_B(rex);
|
||||
}
|
||||
|
||||
if (insn.modrm.nbytes) {
|
||||
modrm = insn.modrm.bytes[0];
|
||||
if (ins.modrm.nbytes) {
|
||||
modrm = ins.modrm.bytes[0];
|
||||
modrm_mod = X86_MODRM_MOD(modrm);
|
||||
modrm_reg = X86_MODRM_REG(modrm) + 8*rex_r;
|
||||
modrm_rm = X86_MODRM_RM(modrm) + 8*rex_b;
|
||||
}
|
||||
|
||||
if (insn.sib.nbytes) {
|
||||
sib = insn.sib.bytes[0];
|
||||
if (ins.sib.nbytes) {
|
||||
sib = ins.sib.bytes[0];
|
||||
/* sib_scale = X86_SIB_SCALE(sib); */
|
||||
sib_index = X86_SIB_INDEX(sib) + 8*rex_x;
|
||||
sib_base = X86_SIB_BASE(sib) + 8*rex_b;
|
||||
|
@ -254,7 +253,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
break;
|
||||
|
||||
case 0x70 ... 0x7f:
|
||||
*type = INSN_JUMP_CONDITIONAL;
|
||||
insn->type = INSN_JUMP_CONDITIONAL;
|
||||
break;
|
||||
|
||||
case 0x80 ... 0x83:
|
||||
|
@ -278,7 +277,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
if (!rm_is_reg(CFI_SP))
|
||||
break;
|
||||
|
||||
imm = insn.immediate.value;
|
||||
imm = ins.immediate.value;
|
||||
if (op1 & 2) { /* sign extend */
|
||||
if (op1 & 1) { /* imm32 */
|
||||
imm <<= 32;
|
||||
|
@ -309,7 +308,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
ADD_OP(op) {
|
||||
op->src.type = OP_SRC_AND;
|
||||
op->src.reg = CFI_SP;
|
||||
op->src.offset = insn.immediate.value;
|
||||
op->src.offset = ins.immediate.value;
|
||||
op->dest.type = OP_DEST_REG;
|
||||
op->dest.reg = CFI_SP;
|
||||
}
|
||||
|
@ -356,7 +355,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
op->src.reg = CFI_SP;
|
||||
op->dest.type = OP_DEST_REG_INDIRECT;
|
||||
op->dest.reg = modrm_rm;
|
||||
op->dest.offset = insn.displacement.value;
|
||||
op->dest.offset = ins.displacement.value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -389,7 +388,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
op->src.reg = modrm_reg;
|
||||
op->dest.type = OP_DEST_REG_INDIRECT;
|
||||
op->dest.reg = CFI_BP;
|
||||
op->dest.offset = insn.displacement.value;
|
||||
op->dest.offset = ins.displacement.value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -402,7 +401,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
op->src.reg = modrm_reg;
|
||||
op->dest.type = OP_DEST_REG_INDIRECT;
|
||||
op->dest.reg = CFI_SP;
|
||||
op->dest.offset = insn.displacement.value;
|
||||
op->dest.offset = ins.displacement.value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -419,7 +418,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
ADD_OP(op) {
|
||||
op->src.type = OP_SRC_REG_INDIRECT;
|
||||
op->src.reg = CFI_BP;
|
||||
op->src.offset = insn.displacement.value;
|
||||
op->src.offset = ins.displacement.value;
|
||||
op->dest.type = OP_DEST_REG;
|
||||
op->dest.reg = modrm_reg;
|
||||
}
|
||||
|
@ -432,7 +431,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
ADD_OP(op) {
|
||||
op->src.type = OP_SRC_REG_INDIRECT;
|
||||
op->src.reg = CFI_SP;
|
||||
op->src.offset = insn.displacement.value;
|
||||
op->src.offset = ins.displacement.value;
|
||||
op->dest.type = OP_DEST_REG;
|
||||
op->dest.reg = modrm_reg;
|
||||
}
|
||||
|
@ -464,7 +463,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
|
||||
/* lea disp(%src), %dst */
|
||||
ADD_OP(op) {
|
||||
op->src.offset = insn.displacement.value;
|
||||
op->src.offset = ins.displacement.value;
|
||||
if (!op->src.offset) {
|
||||
/* lea (%src), %dst */
|
||||
op->src.type = OP_SRC_REG;
|
||||
|
@ -487,7 +486,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
break;
|
||||
|
||||
case 0x90:
|
||||
*type = INSN_NOP;
|
||||
insn->type = INSN_NOP;
|
||||
break;
|
||||
|
||||
case 0x9c:
|
||||
|
@ -511,39 +510,39 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
if (op2 == 0x01) {
|
||||
|
||||
if (modrm == 0xca)
|
||||
*type = INSN_CLAC;
|
||||
insn->type = INSN_CLAC;
|
||||
else if (modrm == 0xcb)
|
||||
*type = INSN_STAC;
|
||||
insn->type = INSN_STAC;
|
||||
|
||||
} else if (op2 >= 0x80 && op2 <= 0x8f) {
|
||||
|
||||
*type = INSN_JUMP_CONDITIONAL;
|
||||
insn->type = INSN_JUMP_CONDITIONAL;
|
||||
|
||||
} else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 ||
|
||||
op2 == 0x35) {
|
||||
|
||||
/* sysenter, sysret */
|
||||
*type = INSN_CONTEXT_SWITCH;
|
||||
insn->type = INSN_CONTEXT_SWITCH;
|
||||
|
||||
} else if (op2 == 0x0b || op2 == 0xb9) {
|
||||
|
||||
/* ud2 */
|
||||
*type = INSN_BUG;
|
||||
insn->type = INSN_BUG;
|
||||
|
||||
} else if (op2 == 0x0d || op2 == 0x1f) {
|
||||
|
||||
/* nopl/nopw */
|
||||
*type = INSN_NOP;
|
||||
insn->type = INSN_NOP;
|
||||
|
||||
} else if (op2 == 0x1e) {
|
||||
|
||||
if (prefix == 0xf3 && (modrm == 0xfa || modrm == 0xfb))
|
||||
*type = INSN_ENDBR;
|
||||
insn->type = INSN_ENDBR;
|
||||
|
||||
|
||||
} else if (op2 == 0x38 && op3 == 0xf8) {
|
||||
if (insn.prefixes.nbytes == 1 &&
|
||||
insn.prefixes.bytes[0] == 0xf2) {
|
||||
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);
|
||||
|
@ -591,29 +590,29 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
|
||||
case 0xcc:
|
||||
/* int3 */
|
||||
*type = INSN_TRAP;
|
||||
insn->type = INSN_TRAP;
|
||||
break;
|
||||
|
||||
case 0xe3:
|
||||
/* jecxz/jrcxz */
|
||||
*type = INSN_JUMP_CONDITIONAL;
|
||||
insn->type = INSN_JUMP_CONDITIONAL;
|
||||
break;
|
||||
|
||||
case 0xe9:
|
||||
case 0xeb:
|
||||
*type = INSN_JUMP_UNCONDITIONAL;
|
||||
insn->type = INSN_JUMP_UNCONDITIONAL;
|
||||
break;
|
||||
|
||||
case 0xc2:
|
||||
case 0xc3:
|
||||
*type = INSN_RETURN;
|
||||
insn->type = INSN_RETURN;
|
||||
break;
|
||||
|
||||
case 0xc7: /* mov imm, r/m */
|
||||
if (!opts.noinstr)
|
||||
break;
|
||||
|
||||
if (insn.length == 3+4+4 && !strncmp(sec->name, ".init.text", 10)) {
|
||||
if (ins.length == 3+4+4 && !strncmp(sec->name, ".init.text", 10)) {
|
||||
struct reloc *immr, *disp;
|
||||
struct symbol *func;
|
||||
int idx;
|
||||
|
@ -661,17 +660,17 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
|
||||
case 0xca: /* retf */
|
||||
case 0xcb: /* retf */
|
||||
*type = INSN_CONTEXT_SWITCH;
|
||||
insn->type = INSN_CONTEXT_SWITCH;
|
||||
break;
|
||||
|
||||
case 0xe0: /* loopne */
|
||||
case 0xe1: /* loope */
|
||||
case 0xe2: /* loop */
|
||||
*type = INSN_JUMP_CONDITIONAL;
|
||||
insn->type = INSN_JUMP_CONDITIONAL;
|
||||
break;
|
||||
|
||||
case 0xe8:
|
||||
*type = INSN_CALL;
|
||||
insn->type = INSN_CALL;
|
||||
/*
|
||||
* For the impact on the stack, a CALL behaves like
|
||||
* a PUSH of an immediate value (the return address).
|
||||
|
@ -683,30 +682,30 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
break;
|
||||
|
||||
case 0xfc:
|
||||
*type = INSN_CLD;
|
||||
insn->type = INSN_CLD;
|
||||
break;
|
||||
|
||||
case 0xfd:
|
||||
*type = INSN_STD;
|
||||
insn->type = INSN_STD;
|
||||
break;
|
||||
|
||||
case 0xff:
|
||||
if (modrm_reg == 2 || modrm_reg == 3) {
|
||||
|
||||
*type = INSN_CALL_DYNAMIC;
|
||||
if (has_notrack_prefix(&insn))
|
||||
insn->type = INSN_CALL_DYNAMIC;
|
||||
if (has_notrack_prefix(&ins))
|
||||
WARN("notrack prefix found at %s:0x%lx", sec->name, offset);
|
||||
|
||||
} else if (modrm_reg == 4) {
|
||||
|
||||
*type = INSN_JUMP_DYNAMIC;
|
||||
if (has_notrack_prefix(&insn))
|
||||
insn->type = INSN_JUMP_DYNAMIC;
|
||||
if (has_notrack_prefix(&ins))
|
||||
WARN("notrack prefix found at %s:0x%lx", sec->name, offset);
|
||||
|
||||
} else if (modrm_reg == 5) {
|
||||
|
||||
/* jmpf */
|
||||
*type = INSN_CONTEXT_SWITCH;
|
||||
insn->type = INSN_CONTEXT_SWITCH;
|
||||
|
||||
} else if (modrm_reg == 6) {
|
||||
|
||||
|
@ -723,7 +722,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
break;
|
||||
}
|
||||
|
||||
*immediate = insn.immediate.nbytes ? insn.immediate.value : 0;
|
||||
insn->immediate = ins.immediate.nbytes ? ins.immediate.value : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ static int parse_hacks(const struct option *opt, const char *str, int unset)
|
|||
return found ? 0 : -1;
|
||||
}
|
||||
|
||||
const struct option check_options[] = {
|
||||
static const struct option check_options[] = {
|
||||
OPT_GROUP("Actions:"),
|
||||
OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),
|
||||
OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include <linux/static_call_types.h>
|
||||
|
||||
struct alternative {
|
||||
struct list_head list;
|
||||
struct alternative *next;
|
||||
struct instruction *insn;
|
||||
bool skip_orig;
|
||||
};
|
||||
|
@ -47,27 +47,29 @@ struct instruction *find_insn(struct objtool_file *file,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static struct instruction *next_insn_same_sec(struct objtool_file *file,
|
||||
struct instruction *insn)
|
||||
struct instruction *next_insn_same_sec(struct objtool_file *file,
|
||||
struct instruction *insn)
|
||||
{
|
||||
struct instruction *next = list_next_entry(insn, list);
|
||||
if (insn->idx == INSN_CHUNK_MAX)
|
||||
return find_insn(file, insn->sec, insn->offset + insn->len);
|
||||
|
||||
if (!next || &next->list == &file->insn_list || next->sec != insn->sec)
|
||||
insn++;
|
||||
if (!insn->len)
|
||||
return NULL;
|
||||
|
||||
return next;
|
||||
return insn;
|
||||
}
|
||||
|
||||
static struct instruction *next_insn_same_func(struct objtool_file *file,
|
||||
struct instruction *insn)
|
||||
{
|
||||
struct instruction *next = list_next_entry(insn, list);
|
||||
struct instruction *next = next_insn_same_sec(file, insn);
|
||||
struct symbol *func = insn_func(insn);
|
||||
|
||||
if (!func)
|
||||
return NULL;
|
||||
|
||||
if (&next->list != &file->insn_list && insn_func(next) == func)
|
||||
if (next && insn_func(next) == func)
|
||||
return next;
|
||||
|
||||
/* Check if we're already in the subfunction: */
|
||||
|
@ -78,17 +80,35 @@ static struct instruction *next_insn_same_func(struct objtool_file *file,
|
|||
return find_insn(file, func->cfunc->sec, func->cfunc->offset);
|
||||
}
|
||||
|
||||
static struct instruction *prev_insn_same_sym(struct objtool_file *file,
|
||||
struct instruction *insn)
|
||||
static struct instruction *prev_insn_same_sec(struct objtool_file *file,
|
||||
struct instruction *insn)
|
||||
{
|
||||
struct instruction *prev = list_prev_entry(insn, list);
|
||||
if (insn->idx == 0) {
|
||||
if (insn->prev_len)
|
||||
return find_insn(file, insn->sec, insn->offset - insn->prev_len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (&prev->list != &file->insn_list && insn_func(prev) == insn_func(insn))
|
||||
return insn - 1;
|
||||
}
|
||||
|
||||
static struct instruction *prev_insn_same_sym(struct objtool_file *file,
|
||||
struct instruction *insn)
|
||||
{
|
||||
struct instruction *prev = prev_insn_same_sec(file, insn);
|
||||
|
||||
if (prev && insn_func(prev) == insn_func(insn))
|
||||
return prev;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define for_each_insn(file, insn) \
|
||||
for (struct section *__sec, *__fake = (struct section *)1; \
|
||||
__fake; __fake = NULL) \
|
||||
for_each_sec(file, __sec) \
|
||||
sec_for_each_insn(file, __sec, insn)
|
||||
|
||||
#define func_for_each_insn(file, func, insn) \
|
||||
for (insn = find_insn(file, func->sec, func->offset); \
|
||||
insn; \
|
||||
|
@ -96,16 +116,13 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,
|
|||
|
||||
#define sym_for_each_insn(file, sym, insn) \
|
||||
for (insn = find_insn(file, sym->sec, sym->offset); \
|
||||
insn && &insn->list != &file->insn_list && \
|
||||
insn->sec == sym->sec && \
|
||||
insn->offset < sym->offset + sym->len; \
|
||||
insn = list_next_entry(insn, list))
|
||||
insn && insn->offset < sym->offset + sym->len; \
|
||||
insn = next_insn_same_sec(file, insn))
|
||||
|
||||
#define sym_for_each_insn_continue_reverse(file, sym, insn) \
|
||||
for (insn = list_prev_entry(insn, list); \
|
||||
&insn->list != &file->insn_list && \
|
||||
insn->sec == sym->sec && insn->offset >= sym->offset; \
|
||||
insn = list_prev_entry(insn, list))
|
||||
for (insn = prev_insn_same_sec(file, insn); \
|
||||
insn && insn->offset >= sym->offset; \
|
||||
insn = prev_insn_same_sec(file, insn))
|
||||
|
||||
#define sec_for_each_insn_from(file, insn) \
|
||||
for (; insn; insn = next_insn_same_sec(file, insn))
|
||||
|
@ -114,16 +131,34 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,
|
|||
for (insn = next_insn_same_sec(file, insn); insn; \
|
||||
insn = next_insn_same_sec(file, insn))
|
||||
|
||||
static inline struct symbol *insn_call_dest(struct instruction *insn)
|
||||
{
|
||||
if (insn->type == INSN_JUMP_DYNAMIC ||
|
||||
insn->type == INSN_CALL_DYNAMIC)
|
||||
return NULL;
|
||||
|
||||
return insn->_call_dest;
|
||||
}
|
||||
|
||||
static inline struct reloc *insn_jump_table(struct instruction *insn)
|
||||
{
|
||||
if (insn->type == INSN_JUMP_DYNAMIC ||
|
||||
insn->type == INSN_CALL_DYNAMIC)
|
||||
return insn->_jump_table;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool is_jump_table_jump(struct instruction *insn)
|
||||
{
|
||||
struct alt_group *alt_group = insn->alt_group;
|
||||
|
||||
if (insn->jump_table)
|
||||
if (insn_jump_table(insn))
|
||||
return true;
|
||||
|
||||
/* Retpoline alternative for a jump table? */
|
||||
return alt_group && alt_group->orig_group &&
|
||||
alt_group->orig_group->first_insn->jump_table;
|
||||
insn_jump_table(alt_group->orig_group->first_insn);
|
||||
}
|
||||
|
||||
static bool is_sibling_call(struct instruction *insn)
|
||||
|
@ -137,8 +172,8 @@ static bool is_sibling_call(struct instruction *insn)
|
|||
return !is_jump_table_jump(insn);
|
||||
}
|
||||
|
||||
/* add_jump_destinations() sets insn->call_dest for sibling calls. */
|
||||
return (is_static_jump(insn) && insn->call_dest);
|
||||
/* add_jump_destinations() sets insn_call_dest(insn) for sibling calls. */
|
||||
return (is_static_jump(insn) && insn_call_dest(insn));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -274,8 +309,8 @@ static void init_insn_state(struct objtool_file *file, struct insn_state *state,
|
|||
|
||||
/*
|
||||
* We need the full vmlinux for noinstr validation, otherwise we can
|
||||
* not correctly determine insn->call_dest->sec (external symbols do
|
||||
* not have a section).
|
||||
* not correctly determine insn_call_dest(insn)->sec (external symbols
|
||||
* do not have a section).
|
||||
*/
|
||||
if (opts.link && opts.noinstr && sec)
|
||||
state->noinstr = sec->noinstr;
|
||||
|
@ -366,6 +401,9 @@ static int decode_instructions(struct objtool_file *file)
|
|||
int ret;
|
||||
|
||||
for_each_sec(file, sec) {
|
||||
struct instruction *insns = NULL;
|
||||
u8 prev_len = 0;
|
||||
u8 idx = 0;
|
||||
|
||||
if (!(sec->sh.sh_flags & SHF_EXECINSTR))
|
||||
continue;
|
||||
|
@ -391,26 +429,31 @@ static int decode_instructions(struct objtool_file *file)
|
|||
sec->init = true;
|
||||
|
||||
for (offset = 0; offset < sec->sh.sh_size; offset += insn->len) {
|
||||
insn = malloc(sizeof(*insn));
|
||||
if (!insn) {
|
||||
WARN("malloc failed");
|
||||
return -1;
|
||||
if (!insns || idx == INSN_CHUNK_MAX) {
|
||||
insns = calloc(sizeof(*insn), INSN_CHUNK_SIZE);
|
||||
if (!insns) {
|
||||
WARN("malloc failed");
|
||||
return -1;
|
||||
}
|
||||
idx = 0;
|
||||
} else {
|
||||
idx++;
|
||||
}
|
||||
memset(insn, 0, sizeof(*insn));
|
||||
INIT_LIST_HEAD(&insn->alts);
|
||||
INIT_LIST_HEAD(&insn->stack_ops);
|
||||
INIT_LIST_HEAD(&insn->call_node);
|
||||
insn = &insns[idx];
|
||||
insn->idx = idx;
|
||||
|
||||
INIT_LIST_HEAD(&insn->call_node);
|
||||
insn->sec = sec;
|
||||
insn->offset = offset;
|
||||
insn->prev_len = prev_len;
|
||||
|
||||
ret = arch_decode_instruction(file, sec, offset,
|
||||
sec->sh.sh_size - offset,
|
||||
&insn->len, &insn->type,
|
||||
&insn->immediate,
|
||||
&insn->stack_ops);
|
||||
insn);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
prev_len = insn->len;
|
||||
|
||||
/*
|
||||
* By default, "ud2" is a dead end unless otherwise
|
||||
|
@ -421,10 +464,11 @@ static int decode_instructions(struct objtool_file *file)
|
|||
insn->dead_end = true;
|
||||
|
||||
hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset));
|
||||
list_add_tail(&insn->list, &file->insn_list);
|
||||
nr_insns++;
|
||||
}
|
||||
|
||||
// printf("%s: last chunk used: %d\n", sec->name, (int)idx);
|
||||
|
||||
list_for_each_entry(func, &sec->symbol_list, list) {
|
||||
if (func->type != STT_NOTYPE && func->type != STT_FUNC)
|
||||
continue;
|
||||
|
@ -467,10 +511,6 @@ static int decode_instructions(struct objtool_file *file)
|
|||
printf("nr_insns: %lu\n", nr_insns);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
free(insn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -585,7 +625,7 @@ static int add_dead_ends(struct objtool_file *file)
|
|||
}
|
||||
insn = find_insn(file, reloc->sym->sec, reloc->addend);
|
||||
if (insn)
|
||||
insn = list_prev_entry(insn, list);
|
||||
insn = prev_insn_same_sec(file, insn);
|
||||
else if (reloc->addend == reloc->sym->sec->sh.sh_size) {
|
||||
insn = find_last_insn(file, reloc->sym->sec);
|
||||
if (!insn) {
|
||||
|
@ -620,7 +660,7 @@ reachable:
|
|||
}
|
||||
insn = find_insn(file, reloc->sym->sec, reloc->addend);
|
||||
if (insn)
|
||||
insn = list_prev_entry(insn, list);
|
||||
insn = prev_insn_same_sec(file, insn);
|
||||
else if (reloc->addend == reloc->sym->sec->sh.sh_size) {
|
||||
insn = find_last_insn(file, reloc->sym->sec);
|
||||
if (!insn) {
|
||||
|
@ -682,7 +722,7 @@ static int create_static_call_sections(struct objtool_file *file)
|
|||
return -1;
|
||||
|
||||
/* find key symbol */
|
||||
key_name = strdup(insn->call_dest->name);
|
||||
key_name = strdup(insn_call_dest(insn)->name);
|
||||
if (!key_name) {
|
||||
perror("strdup");
|
||||
return -1;
|
||||
|
@ -690,6 +730,7 @@ static int create_static_call_sections(struct objtool_file *file)
|
|||
if (strncmp(key_name, STATIC_CALL_TRAMP_PREFIX_STR,
|
||||
STATIC_CALL_TRAMP_PREFIX_LEN)) {
|
||||
WARN("static_call: trampoline name malformed: %s", key_name);
|
||||
free(key_name);
|
||||
return -1;
|
||||
}
|
||||
tmp = key_name + STATIC_CALL_TRAMP_PREFIX_LEN - STATIC_CALL_KEY_PREFIX_LEN;
|
||||
|
@ -699,6 +740,7 @@ static int create_static_call_sections(struct objtool_file *file)
|
|||
if (!key_sym) {
|
||||
if (!opts.module) {
|
||||
WARN("static_call: can't find static_call_key symbol: %s", tmp);
|
||||
free(key_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -711,7 +753,7 @@ static int create_static_call_sections(struct objtool_file *file)
|
|||
* trampoline address. This is fixed up in
|
||||
* static_call_add_module().
|
||||
*/
|
||||
key_sym = insn->call_dest;
|
||||
key_sym = insn_call_dest(insn);
|
||||
}
|
||||
free(key_name);
|
||||
|
||||
|
@ -856,8 +898,15 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file)
|
|||
list_for_each_entry(insn, &file->endbr_list, call_node) {
|
||||
|
||||
int *site = (int *)sec->data->d_buf + idx;
|
||||
struct symbol *sym = insn->sym;
|
||||
*site = 0;
|
||||
|
||||
if (opts.module && sym && sym->type == STT_FUNC &&
|
||||
insn->offset == sym->offset &&
|
||||
(!strcmp(sym->name, "init_module") ||
|
||||
!strcmp(sym->name, "cleanup_module")))
|
||||
WARN("%s(): not an indirect call target", sym->name);
|
||||
|
||||
if (elf_add_reloc_to_insn(file->elf, sec,
|
||||
idx * sizeof(int),
|
||||
R_X86_64_PC32,
|
||||
|
@ -1302,43 +1351,42 @@ __weak bool arch_is_rethunk(struct symbol *sym)
|
|||
return false;
|
||||
}
|
||||
|
||||
#define NEGATIVE_RELOC ((void *)-1L)
|
||||
|
||||
static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn)
|
||||
{
|
||||
if (insn->reloc == NEGATIVE_RELOC)
|
||||
struct reloc *reloc;
|
||||
|
||||
if (insn->no_reloc)
|
||||
return NULL;
|
||||
|
||||
if (!insn->reloc) {
|
||||
if (!file)
|
||||
return NULL;
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
insn->reloc = find_reloc_by_dest_range(file->elf, insn->sec,
|
||||
insn->offset, insn->len);
|
||||
if (!insn->reloc) {
|
||||
insn->reloc = NEGATIVE_RELOC;
|
||||
return NULL;
|
||||
}
|
||||
reloc = find_reloc_by_dest_range(file->elf, insn->sec,
|
||||
insn->offset, insn->len);
|
||||
if (!reloc) {
|
||||
insn->no_reloc = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return insn->reloc;
|
||||
return reloc;
|
||||
}
|
||||
|
||||
static void remove_insn_ops(struct instruction *insn)
|
||||
{
|
||||
struct stack_op *op, *tmp;
|
||||
struct stack_op *op, *next;
|
||||
|
||||
list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) {
|
||||
list_del(&op->list);
|
||||
for (op = insn->stack_ops; op; op = next) {
|
||||
next = op->next;
|
||||
free(op);
|
||||
}
|
||||
insn->stack_ops = NULL;
|
||||
}
|
||||
|
||||
static void annotate_call_site(struct objtool_file *file,
|
||||
struct instruction *insn, bool sibling)
|
||||
{
|
||||
struct reloc *reloc = insn_reloc(file, insn);
|
||||
struct symbol *sym = insn->call_dest;
|
||||
struct symbol *sym = insn_call_dest(insn);
|
||||
|
||||
if (!sym)
|
||||
sym = reloc->sym;
|
||||
|
@ -1423,7 +1471,7 @@ static void annotate_call_site(struct objtool_file *file,
|
|||
static void add_call_dest(struct objtool_file *file, struct instruction *insn,
|
||||
struct symbol *dest, bool sibling)
|
||||
{
|
||||
insn->call_dest = dest;
|
||||
insn->_call_dest = dest;
|
||||
if (!dest)
|
||||
return;
|
||||
|
||||
|
@ -1681,12 +1729,12 @@ static int add_call_destinations(struct objtool_file *file)
|
|||
if (insn->ignore)
|
||||
continue;
|
||||
|
||||
if (!insn->call_dest) {
|
||||
if (!insn_call_dest(insn)) {
|
||||
WARN_FUNC("unannotated intra-function call", insn->sec, insn->offset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (insn_func(insn) && insn->call_dest->type != STT_FUNC) {
|
||||
if (insn_func(insn) && insn_call_dest(insn)->type != STT_FUNC) {
|
||||
WARN_FUNC("unsupported call to non-function",
|
||||
insn->sec, insn->offset);
|
||||
return -1;
|
||||
|
@ -1724,36 +1772,50 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
struct instruction *orig_insn,
|
||||
struct instruction **new_insn)
|
||||
{
|
||||
struct instruction *last_orig_insn, *last_new_insn = NULL, *insn, *nop = NULL;
|
||||
struct instruction *last_new_insn = NULL, *insn, *nop = NULL;
|
||||
struct alt_group *orig_alt_group, *new_alt_group;
|
||||
unsigned long dest_off;
|
||||
|
||||
|
||||
orig_alt_group = malloc(sizeof(*orig_alt_group));
|
||||
orig_alt_group = orig_insn->alt_group;
|
||||
if (!orig_alt_group) {
|
||||
WARN("malloc failed");
|
||||
return -1;
|
||||
}
|
||||
orig_alt_group->cfi = calloc(special_alt->orig_len,
|
||||
sizeof(struct cfi_state *));
|
||||
if (!orig_alt_group->cfi) {
|
||||
WARN("calloc failed");
|
||||
return -1;
|
||||
}
|
||||
struct instruction *last_orig_insn = NULL;
|
||||
|
||||
last_orig_insn = NULL;
|
||||
insn = orig_insn;
|
||||
sec_for_each_insn_from(file, insn) {
|
||||
if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
|
||||
break;
|
||||
orig_alt_group = malloc(sizeof(*orig_alt_group));
|
||||
if (!orig_alt_group) {
|
||||
WARN("malloc failed");
|
||||
return -1;
|
||||
}
|
||||
orig_alt_group->cfi = calloc(special_alt->orig_len,
|
||||
sizeof(struct cfi_state *));
|
||||
if (!orig_alt_group->cfi) {
|
||||
WARN("calloc failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
insn->alt_group = orig_alt_group;
|
||||
last_orig_insn = insn;
|
||||
insn = orig_insn;
|
||||
sec_for_each_insn_from(file, insn) {
|
||||
if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
|
||||
break;
|
||||
|
||||
insn->alt_group = orig_alt_group;
|
||||
last_orig_insn = insn;
|
||||
}
|
||||
orig_alt_group->orig_group = NULL;
|
||||
orig_alt_group->first_insn = orig_insn;
|
||||
orig_alt_group->last_insn = last_orig_insn;
|
||||
orig_alt_group->nop = NULL;
|
||||
} else {
|
||||
if (orig_alt_group->last_insn->offset + orig_alt_group->last_insn->len -
|
||||
orig_alt_group->first_insn->offset != special_alt->orig_len) {
|
||||
WARN_FUNC("weirdly overlapping alternative! %ld != %d",
|
||||
orig_insn->sec, orig_insn->offset,
|
||||
orig_alt_group->last_insn->offset +
|
||||
orig_alt_group->last_insn->len -
|
||||
orig_alt_group->first_insn->offset,
|
||||
special_alt->orig_len);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
orig_alt_group->orig_group = NULL;
|
||||
orig_alt_group->first_insn = orig_insn;
|
||||
orig_alt_group->last_insn = last_orig_insn;
|
||||
|
||||
|
||||
new_alt_group = malloc(sizeof(*new_alt_group));
|
||||
if (!new_alt_group) {
|
||||
|
@ -1775,8 +1837,6 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
return -1;
|
||||
}
|
||||
memset(nop, 0, sizeof(*nop));
|
||||
INIT_LIST_HEAD(&nop->alts);
|
||||
INIT_LIST_HEAD(&nop->stack_ops);
|
||||
|
||||
nop->sec = special_alt->new_sec;
|
||||
nop->offset = special_alt->new_off + special_alt->new_len;
|
||||
|
@ -1830,7 +1890,7 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
|
||||
dest_off = arch_jump_destination(insn);
|
||||
if (dest_off == special_alt->new_off + special_alt->new_len) {
|
||||
insn->jump_dest = next_insn_same_sec(file, last_orig_insn);
|
||||
insn->jump_dest = next_insn_same_sec(file, orig_alt_group->last_insn);
|
||||
if (!insn->jump_dest) {
|
||||
WARN_FUNC("can't find alternative jump destination",
|
||||
insn->sec, insn->offset);
|
||||
|
@ -1845,12 +1905,11 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (nop)
|
||||
list_add(&nop->list, &last_new_insn->list);
|
||||
end:
|
||||
new_alt_group->orig_group = orig_alt_group;
|
||||
new_alt_group->first_insn = *new_insn;
|
||||
new_alt_group->last_insn = nop ? : last_new_insn;
|
||||
new_alt_group->last_insn = last_new_insn;
|
||||
new_alt_group->nop = nop;
|
||||
new_alt_group->cfi = orig_alt_group->cfi;
|
||||
return 0;
|
||||
}
|
||||
|
@ -1900,7 +1959,7 @@ static int handle_jump_alt(struct objtool_file *file,
|
|||
else
|
||||
file->jl_long++;
|
||||
|
||||
*new_insn = list_next_entry(orig_insn, list);
|
||||
*new_insn = next_insn_same_sec(file, orig_insn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1974,7 +2033,8 @@ static int add_special_section_alts(struct objtool_file *file)
|
|||
alt->insn = new_insn;
|
||||
alt->skip_orig = special_alt->skip_orig;
|
||||
orig_insn->ignore_alts |= special_alt->skip_alt;
|
||||
list_add_tail(&alt->list, &orig_insn->alts);
|
||||
alt->next = orig_insn->alts;
|
||||
orig_insn->alts = alt;
|
||||
|
||||
list_del(&special_alt->list);
|
||||
free(special_alt);
|
||||
|
@ -2033,7 +2093,8 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
|
|||
}
|
||||
|
||||
alt->insn = dest_insn;
|
||||
list_add_tail(&alt->list, &insn->alts);
|
||||
alt->next = insn->alts;
|
||||
insn->alts = alt;
|
||||
prev_offset = reloc->offset;
|
||||
}
|
||||
|
||||
|
@ -2123,7 +2184,7 @@ static void mark_func_jump_tables(struct objtool_file *file,
|
|||
reloc = find_jump_table(file, func, insn);
|
||||
if (reloc) {
|
||||
reloc->jump_table_start = true;
|
||||
insn->jump_table = reloc;
|
||||
insn->_jump_table = reloc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2135,10 +2196,10 @@ static int add_func_jump_tables(struct objtool_file *file,
|
|||
int ret;
|
||||
|
||||
func_for_each_insn(file, func, insn) {
|
||||
if (!insn->jump_table)
|
||||
if (!insn_jump_table(insn))
|
||||
continue;
|
||||
|
||||
ret = add_jump_table(file, insn, insn->jump_table);
|
||||
ret = add_jump_table(file, insn, insn_jump_table(insn));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -2271,6 +2332,7 @@ static int read_unwind_hints(struct objtool_file *file)
|
|||
|
||||
cfi.cfa.offset = bswap_if_needed(file->elf, hint->sp_offset);
|
||||
cfi.type = hint->type;
|
||||
cfi.signal = hint->signal;
|
||||
cfi.end = hint->end;
|
||||
|
||||
insn->cfi = cfi_hash_find_or_add(&cfi);
|
||||
|
@ -2610,8 +2672,8 @@ static int decode_sections(struct objtool_file *file)
|
|||
static bool is_fentry_call(struct instruction *insn)
|
||||
{
|
||||
if (insn->type == INSN_CALL &&
|
||||
insn->call_dest &&
|
||||
insn->call_dest->fentry)
|
||||
insn_call_dest(insn) &&
|
||||
insn_call_dest(insn)->fentry)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
@ -3206,8 +3268,12 @@ static int propagate_alt_cfi(struct objtool_file *file, struct instruction *insn
|
|||
alt_cfi[group_off] = insn->cfi;
|
||||
} else {
|
||||
if (cficmp(alt_cfi[group_off], insn->cfi)) {
|
||||
WARN_FUNC("stack layout conflict in alternatives",
|
||||
insn->sec, insn->offset);
|
||||
struct alt_group *orig_group = insn->alt_group->orig_group ?: insn->alt_group;
|
||||
struct instruction *orig = orig_group->first_insn;
|
||||
char *where = offstr(insn->sec, insn->offset);
|
||||
WARN_FUNC("stack layout conflict in alternatives: %s",
|
||||
orig->sec, orig->offset, where);
|
||||
free(where);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -3221,7 +3287,7 @@ static int handle_insn_ops(struct instruction *insn,
|
|||
{
|
||||
struct stack_op *op;
|
||||
|
||||
list_for_each_entry(op, &insn->stack_ops, list) {
|
||||
for (op = insn->stack_ops; op; op = op->next) {
|
||||
|
||||
if (update_cfi_state(insn, next_insn, &state->cfi, op))
|
||||
return 1;
|
||||
|
@ -3318,8 +3384,8 @@ static inline const char *call_dest_name(struct instruction *insn)
|
|||
struct reloc *rel;
|
||||
int idx;
|
||||
|
||||
if (insn->call_dest)
|
||||
return insn->call_dest->name;
|
||||
if (insn_call_dest(insn))
|
||||
return insn_call_dest(insn)->name;
|
||||
|
||||
rel = insn_reloc(NULL, insn);
|
||||
if (rel && !strcmp(rel->sym->name, "pv_ops")) {
|
||||
|
@ -3401,13 +3467,13 @@ static int validate_call(struct objtool_file *file,
|
|||
struct insn_state *state)
|
||||
{
|
||||
if (state->noinstr && state->instr <= 0 &&
|
||||
!noinstr_call_dest(file, insn, insn->call_dest)) {
|
||||
!noinstr_call_dest(file, insn, insn_call_dest(insn))) {
|
||||
WARN_FUNC("call to %s() leaves .noinstr.text section",
|
||||
insn->sec, insn->offset, call_dest_name(insn));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
|
||||
if (state->uaccess && !func_uaccess_safe(insn_call_dest(insn))) {
|
||||
WARN_FUNC("call to %s() with UACCESS enabled",
|
||||
insn->sec, insn->offset, call_dest_name(insn));
|
||||
return 1;
|
||||
|
@ -3485,11 +3551,28 @@ static struct instruction *next_insn_to_validate(struct objtool_file *file,
|
|||
* Simulate the fact that alternatives are patched in-place. When the
|
||||
* end of a replacement alt_group is reached, redirect objtool flow to
|
||||
* the end of the original alt_group.
|
||||
*
|
||||
* insn->alts->insn -> alt_group->first_insn
|
||||
* ...
|
||||
* alt_group->last_insn
|
||||
* [alt_group->nop] -> next(orig_group->last_insn)
|
||||
*/
|
||||
if (alt_group && insn == alt_group->last_insn && alt_group->orig_group)
|
||||
return next_insn_same_sec(file, alt_group->orig_group->last_insn);
|
||||
if (alt_group) {
|
||||
if (alt_group->nop) {
|
||||
/* ->nop implies ->orig_group */
|
||||
if (insn == alt_group->last_insn)
|
||||
return alt_group->nop;
|
||||
if (insn == alt_group->nop)
|
||||
goto next_orig;
|
||||
}
|
||||
if (insn == alt_group->last_insn && alt_group->orig_group)
|
||||
goto next_orig;
|
||||
}
|
||||
|
||||
return next_insn_same_sec(file, insn);
|
||||
|
||||
next_orig:
|
||||
return next_insn_same_sec(file, alt_group->orig_group->last_insn);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3590,10 +3673,10 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
if (propagate_alt_cfi(file, insn))
|
||||
return 1;
|
||||
|
||||
if (!insn->ignore_alts && !list_empty(&insn->alts)) {
|
||||
if (!insn->ignore_alts && insn->alts) {
|
||||
bool skip_orig = false;
|
||||
|
||||
list_for_each_entry(alt, &insn->alts, list) {
|
||||
for (alt = insn->alts; alt; alt = alt->next) {
|
||||
if (alt->skip_orig)
|
||||
skip_orig = true;
|
||||
|
||||
|
@ -3740,11 +3823,25 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int validate_unwind_hint(struct objtool_file *file,
|
||||
struct instruction *insn,
|
||||
struct insn_state *state)
|
||||
{
|
||||
if (insn->hint && !insn->visited && !insn->ignore) {
|
||||
int ret = validate_branch(file, insn_func(insn), insn, *state);
|
||||
if (ret && opts.backtrace)
|
||||
BT_FUNC("<=== (hint)", insn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
|
||||
{
|
||||
struct instruction *insn;
|
||||
struct insn_state state;
|
||||
int ret, warnings = 0;
|
||||
int warnings = 0;
|
||||
|
||||
if (!file->hints)
|
||||
return 0;
|
||||
|
@ -3752,22 +3849,11 @@ static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
|
|||
init_insn_state(file, &state, sec);
|
||||
|
||||
if (sec) {
|
||||
insn = find_insn(file, sec, 0);
|
||||
if (!insn)
|
||||
return 0;
|
||||
sec_for_each_insn(file, sec, insn)
|
||||
warnings += validate_unwind_hint(file, insn, &state);
|
||||
} else {
|
||||
insn = list_first_entry(&file->insn_list, typeof(*insn), list);
|
||||
}
|
||||
|
||||
while (&insn->list != &file->insn_list && (!sec || insn->sec == sec)) {
|
||||
if (insn->hint && !insn->visited && !insn->ignore) {
|
||||
ret = validate_branch(file, insn_func(insn), insn, state);
|
||||
if (ret && opts.backtrace)
|
||||
BT_FUNC("<=== (hint)", insn);
|
||||
warnings += ret;
|
||||
}
|
||||
|
||||
insn = list_next_entry(insn, list);
|
||||
for_each_insn(file, insn)
|
||||
warnings += validate_unwind_hint(file, insn, &state);
|
||||
}
|
||||
|
||||
return warnings;
|
||||
|
@ -3792,11 +3878,11 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn)
|
|||
|
||||
insn->visited |= VISITED_ENTRY;
|
||||
|
||||
if (!insn->ignore_alts && !list_empty(&insn->alts)) {
|
||||
if (!insn->ignore_alts && insn->alts) {
|
||||
struct alternative *alt;
|
||||
bool skip_orig = false;
|
||||
|
||||
list_for_each_entry(alt, &insn->alts, list) {
|
||||
for (alt = insn->alts; alt; alt = alt->next) {
|
||||
if (alt->skip_orig)
|
||||
skip_orig = true;
|
||||
|
||||
|
@ -3845,11 +3931,11 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn)
|
|||
|
||||
/* fallthrough */
|
||||
case INSN_CALL:
|
||||
dest = find_insn(file, insn->call_dest->sec,
|
||||
insn->call_dest->offset);
|
||||
dest = find_insn(file, insn_call_dest(insn)->sec,
|
||||
insn_call_dest(insn)->offset);
|
||||
if (!dest) {
|
||||
WARN("Unresolved function after linking!?: %s",
|
||||
insn->call_dest->name);
|
||||
insn_call_dest(insn)->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -3950,13 +4036,13 @@ static int validate_retpoline(struct objtool_file *file)
|
|||
static bool is_kasan_insn(struct instruction *insn)
|
||||
{
|
||||
return (insn->type == INSN_CALL &&
|
||||
!strcmp(insn->call_dest->name, "__asan_handle_no_return"));
|
||||
!strcmp(insn_call_dest(insn)->name, "__asan_handle_no_return"));
|
||||
}
|
||||
|
||||
static bool is_ubsan_insn(struct instruction *insn)
|
||||
{
|
||||
return (insn->type == INSN_CALL &&
|
||||
!strcmp(insn->call_dest->name,
|
||||
!strcmp(insn_call_dest(insn)->name,
|
||||
"__ubsan_handle_builtin_unreachable"));
|
||||
}
|
||||
|
||||
|
@ -4033,8 +4119,9 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
|
|||
*
|
||||
* It may also insert a UD2 after calling a __noreturn function.
|
||||
*/
|
||||
prev_insn = list_prev_entry(insn, list);
|
||||
if ((prev_insn->dead_end || dead_end_function(file, prev_insn->call_dest)) &&
|
||||
prev_insn = prev_insn_same_sec(file, insn);
|
||||
if ((prev_insn->dead_end ||
|
||||
dead_end_function(file, insn_call_dest(prev_insn))) &&
|
||||
(insn->type == INSN_BUG ||
|
||||
(insn->type == INSN_JUMP_UNCONDITIONAL &&
|
||||
insn->jump_dest && insn->jump_dest->type == INSN_BUG)))
|
||||
|
@ -4064,7 +4151,7 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
|
|||
if (insn->offset + insn->len >= insn_func(insn)->offset + insn_func(insn)->len)
|
||||
break;
|
||||
|
||||
insn = list_next_entry(insn, list);
|
||||
insn = next_insn_same_sec(file, insn);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -4077,10 +4164,10 @@ static int add_prefix_symbol(struct objtool_file *file, struct symbol *func,
|
|||
return 0;
|
||||
|
||||
for (;;) {
|
||||
struct instruction *prev = list_prev_entry(insn, list);
|
||||
struct instruction *prev = prev_insn_same_sec(file, insn);
|
||||
u64 offset;
|
||||
|
||||
if (&prev->list == &file->insn_list)
|
||||
if (!prev)
|
||||
break;
|
||||
|
||||
if (prev->type != INSN_NOP)
|
||||
|
@ -4479,7 +4566,7 @@ int check(struct objtool_file *file)
|
|||
|
||||
warnings += ret;
|
||||
|
||||
if (list_empty(&file->insn_list))
|
||||
if (!nr_insns)
|
||||
goto out;
|
||||
|
||||
if (opts.retpoline) {
|
||||
|
@ -4588,7 +4675,7 @@ int check(struct objtool_file *file)
|
|||
warnings += ret;
|
||||
}
|
||||
|
||||
if (opts.orc && !list_empty(&file->insn_list)) {
|
||||
if (opts.orc && nr_insns) {
|
||||
ret = orc_create(file);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
|
|
@ -284,13 +284,13 @@ static int read_sections(struct elf *elf)
|
|||
!elf_alloc_hash(section_name, sections_nr))
|
||||
return -1;
|
||||
|
||||
elf->section_data = calloc(sections_nr, sizeof(*sec));
|
||||
if (!elf->section_data) {
|
||||
perror("calloc");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < sections_nr; i++) {
|
||||
sec = malloc(sizeof(*sec));
|
||||
if (!sec) {
|
||||
perror("malloc");
|
||||
return -1;
|
||||
}
|
||||
memset(sec, 0, sizeof(*sec));
|
||||
sec = &elf->section_data[i];
|
||||
|
||||
INIT_LIST_HEAD(&sec->symbol_list);
|
||||
INIT_LIST_HEAD(&sec->reloc_list);
|
||||
|
@ -422,13 +422,13 @@ static int read_symbols(struct elf *elf)
|
|||
!elf_alloc_hash(symbol_name, symbols_nr))
|
||||
return -1;
|
||||
|
||||
elf->symbol_data = calloc(symbols_nr, sizeof(*sym));
|
||||
if (!elf->symbol_data) {
|
||||
perror("calloc");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < symbols_nr; i++) {
|
||||
sym = malloc(sizeof(*sym));
|
||||
if (!sym) {
|
||||
perror("malloc");
|
||||
return -1;
|
||||
}
|
||||
memset(sym, 0, sizeof(*sym));
|
||||
sym = &elf->symbol_data[i];
|
||||
|
||||
sym->idx = i;
|
||||
|
||||
|
@ -918,13 +918,13 @@ static int read_relocs(struct elf *elf)
|
|||
sec->base->reloc = sec;
|
||||
|
||||
nr_reloc = 0;
|
||||
sec->reloc_data = calloc(sec->sh.sh_size / sec->sh.sh_entsize, sizeof(*reloc));
|
||||
if (!sec->reloc_data) {
|
||||
perror("calloc");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) {
|
||||
reloc = malloc(sizeof(*reloc));
|
||||
if (!reloc) {
|
||||
perror("malloc");
|
||||
return -1;
|
||||
}
|
||||
memset(reloc, 0, sizeof(*reloc));
|
||||
reloc = &sec->reloc_data[i];
|
||||
switch (sec->sh.sh_type) {
|
||||
case SHT_REL:
|
||||
if (read_rel_reloc(sec, i, reloc, &symndx))
|
||||
|
@ -1453,16 +1453,16 @@ void elf_close(struct elf *elf)
|
|||
list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) {
|
||||
list_del(&sym->list);
|
||||
hash_del(&sym->hash);
|
||||
free(sym);
|
||||
}
|
||||
list_for_each_entry_safe(reloc, tmpreloc, &sec->reloc_list, list) {
|
||||
list_del(&reloc->list);
|
||||
hash_del(&reloc->hash);
|
||||
free(reloc);
|
||||
}
|
||||
list_del(&sec->list);
|
||||
free(sec);
|
||||
free(sec->reloc_data);
|
||||
}
|
||||
|
||||
free(elf->symbol_data);
|
||||
free(elf->section_data);
|
||||
free(elf);
|
||||
}
|
||||
|
|
|
@ -62,9 +62,9 @@ struct op_src {
|
|||
};
|
||||
|
||||
struct stack_op {
|
||||
struct stack_op *next;
|
||||
struct op_dest dest;
|
||||
struct op_src src;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct instruction;
|
||||
|
@ -75,9 +75,7 @@ void arch_initial_func_cfi_state(struct cfi_init_state *state);
|
|||
|
||||
int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
|
||||
unsigned long offset, unsigned int maxlen,
|
||||
unsigned int *len, enum insn_type *type,
|
||||
unsigned long *immediate,
|
||||
struct list_head *ops_list);
|
||||
struct instruction *insn);
|
||||
|
||||
bool arch_callee_saved_reg(unsigned char reg);
|
||||
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
#include <subcmd/parse-options.h>
|
||||
|
||||
extern const struct option check_options[];
|
||||
|
||||
struct opts {
|
||||
/* actions: */
|
||||
bool dump_orc;
|
||||
|
|
|
@ -34,6 +34,7 @@ struct cfi_state {
|
|||
unsigned char type;
|
||||
bool bp_scratch;
|
||||
bool drap;
|
||||
bool signal;
|
||||
bool end;
|
||||
};
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ struct alt_group {
|
|||
struct alt_group *orig_group;
|
||||
|
||||
/* First and last instructions in the group */
|
||||
struct instruction *first_insn, *last_insn;
|
||||
struct instruction *first_insn, *last_insn, *nop;
|
||||
|
||||
/*
|
||||
* Byte-offset-addressed len-sized array of pointers to CFI structs.
|
||||
|
@ -36,39 +36,46 @@ struct alt_group {
|
|||
struct cfi_state **cfi;
|
||||
};
|
||||
|
||||
#define INSN_CHUNK_BITS 8
|
||||
#define INSN_CHUNK_SIZE (1 << INSN_CHUNK_BITS)
|
||||
#define INSN_CHUNK_MAX (INSN_CHUNK_SIZE - 1)
|
||||
|
||||
struct instruction {
|
||||
struct list_head list;
|
||||
struct hlist_node hash;
|
||||
struct list_head call_node;
|
||||
struct section *sec;
|
||||
unsigned long offset;
|
||||
unsigned int len;
|
||||
enum insn_type type;
|
||||
unsigned long immediate;
|
||||
|
||||
u16 dead_end : 1,
|
||||
ignore : 1,
|
||||
ignore_alts : 1,
|
||||
hint : 1,
|
||||
save : 1,
|
||||
restore : 1,
|
||||
retpoline_safe : 1,
|
||||
noendbr : 1,
|
||||
entry : 1;
|
||||
/* 7 bit hole */
|
||||
|
||||
u8 len;
|
||||
u8 prev_len;
|
||||
u8 type;
|
||||
s8 instr;
|
||||
u8 visited;
|
||||
|
||||
u32 idx : INSN_CHUNK_BITS,
|
||||
dead_end : 1,
|
||||
ignore : 1,
|
||||
ignore_alts : 1,
|
||||
hint : 1,
|
||||
save : 1,
|
||||
restore : 1,
|
||||
retpoline_safe : 1,
|
||||
noendbr : 1,
|
||||
entry : 1,
|
||||
visited : 4,
|
||||
no_reloc : 1;
|
||||
/* 10 bit hole */
|
||||
|
||||
struct alt_group *alt_group;
|
||||
struct symbol *call_dest;
|
||||
struct instruction *jump_dest;
|
||||
struct instruction *first_jump_src;
|
||||
struct reloc *jump_table;
|
||||
struct reloc *reloc;
|
||||
struct list_head alts;
|
||||
union {
|
||||
struct symbol *_call_dest;
|
||||
struct reloc *_jump_table;
|
||||
};
|
||||
struct alternative *alts;
|
||||
struct symbol *sym;
|
||||
struct list_head stack_ops;
|
||||
struct stack_op *stack_ops;
|
||||
struct cfi_state *cfi;
|
||||
};
|
||||
|
||||
|
@ -107,13 +114,11 @@ static inline bool is_jump(struct instruction *insn)
|
|||
struct instruction *find_insn(struct objtool_file *file,
|
||||
struct section *sec, unsigned long offset);
|
||||
|
||||
#define for_each_insn(file, insn) \
|
||||
list_for_each_entry(insn, &file->insn_list, list)
|
||||
struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruction *insn);
|
||||
|
||||
#define sec_for_each_insn(file, sec, insn) \
|
||||
for (insn = find_insn(file, sec, 0); \
|
||||
insn && &insn->list != &file->insn_list && \
|
||||
insn->sec == sec; \
|
||||
insn = list_next_entry(insn, list))
|
||||
#define sec_for_each_insn(file, _sec, insn) \
|
||||
for (insn = find_insn(file, _sec, 0); \
|
||||
insn && insn->sec == _sec; \
|
||||
insn = next_insn_same_sec(file, insn))
|
||||
|
||||
#endif /* _CHECK_H */
|
||||
|
|
|
@ -39,6 +39,7 @@ struct section {
|
|||
char *name;
|
||||
int idx;
|
||||
bool changed, text, rodata, noinstr, init, truncate;
|
||||
struct reloc *reloc_data;
|
||||
};
|
||||
|
||||
struct symbol {
|
||||
|
@ -49,12 +50,11 @@ struct symbol {
|
|||
GElf_Sym sym;
|
||||
struct section *sec;
|
||||
char *name;
|
||||
unsigned int idx;
|
||||
unsigned char bind, type;
|
||||
unsigned int idx, len;
|
||||
unsigned long offset;
|
||||
unsigned int len;
|
||||
unsigned long __subtree_last;
|
||||
struct symbol *pfunc, *cfunc, *alias;
|
||||
unsigned char bind, type;
|
||||
u8 uaccess_safe : 1;
|
||||
u8 static_call_tramp : 1;
|
||||
u8 retpoline_thunk : 1;
|
||||
|
@ -104,6 +104,9 @@ struct elf {
|
|||
struct hlist_head *section_hash;
|
||||
struct hlist_head *section_name_hash;
|
||||
struct hlist_head *reloc_hash;
|
||||
|
||||
struct section *section_data;
|
||||
struct symbol *symbol_data;
|
||||
};
|
||||
|
||||
#define OFFSET_STRIDE_BITS 4
|
||||
|
|
|
@ -21,7 +21,6 @@ struct pv_state {
|
|||
|
||||
struct objtool_file {
|
||||
struct elf *elf;
|
||||
struct list_head insn_list;
|
||||
DECLARE_HASHTABLE(insn_hash, 20);
|
||||
struct list_head retpoline_call_list;
|
||||
struct list_head return_thunk_list;
|
||||
|
|
|
@ -19,6 +19,7 @@ struct special_alt {
|
|||
bool skip_orig;
|
||||
bool skip_alt;
|
||||
bool jump_or_nop;
|
||||
u8 key_addend;
|
||||
|
||||
struct section *orig_sec;
|
||||
unsigned long orig_off;
|
||||
|
@ -27,7 +28,6 @@ struct special_alt {
|
|||
unsigned long new_off;
|
||||
|
||||
unsigned int orig_len, new_len; /* group only */
|
||||
u8 key_addend;
|
||||
};
|
||||
|
||||
int special_get_alts(struct elf *elf, struct list_head *alts);
|
||||
|
|
|
@ -99,7 +99,6 @@ struct objtool_file *objtool_open_read(const char *_objname)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&file.insn_list);
|
||||
hash_init(file.insn_hash);
|
||||
INIT_LIST_HEAD(&file.retpoline_call_list);
|
||||
INIT_LIST_HEAD(&file.return_thunk_list);
|
||||
|
|
|
@ -211,8 +211,8 @@ int orc_dump(const char *_objname)
|
|||
|
||||
print_reg(orc[i].bp_reg, bswap_if_needed(&dummy_elf, orc[i].bp_offset));
|
||||
|
||||
printf(" type:%s end:%d\n",
|
||||
orc_type_name(orc[i].type), orc[i].end);
|
||||
printf(" type:%s signal:%d end:%d\n",
|
||||
orc_type_name(orc[i].type), orc[i].signal, orc[i].end);
|
||||
}
|
||||
|
||||
elf_end(elf);
|
||||
|
|
|
@ -27,6 +27,7 @@ static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi,
|
|||
}
|
||||
|
||||
orc->end = cfi->end;
|
||||
orc->signal = cfi->signal;
|
||||
|
||||
if (cfi->cfa.base == CFI_UNDEFINED) {
|
||||
orc->sp_reg = ORC_REG_UNDEFINED;
|
||||
|
|
|
@ -26,7 +26,7 @@ struct special_entry {
|
|||
unsigned char key; /* jump_label key */
|
||||
};
|
||||
|
||||
struct special_entry entries[] = {
|
||||
static const struct special_entry entries[] = {
|
||||
{
|
||||
.sec = ".altinstructions",
|
||||
.group = true,
|
||||
|
@ -65,7 +65,7 @@ static void reloc_to_sec_off(struct reloc *reloc, struct section **sec,
|
|||
*off = reloc->sym->offset + reloc->addend;
|
||||
}
|
||||
|
||||
static int get_alt_entry(struct elf *elf, struct special_entry *entry,
|
||||
static int get_alt_entry(struct elf *elf, const struct special_entry *entry,
|
||||
struct section *sec, int idx,
|
||||
struct special_alt *alt)
|
||||
{
|
||||
|
@ -139,7 +139,7 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry,
|
|||
*/
|
||||
int special_get_alts(struct elf *elf, struct list_head *alts)
|
||||
{
|
||||
struct special_entry *entry;
|
||||
const struct special_entry *entry;
|
||||
struct section *sec;
|
||||
unsigned int nr_entries;
|
||||
struct special_alt *alt;
|
||||
|
|
Loading…
Reference in New Issue