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:
Linus Torvalds 2023-03-02 09:45:34 -08:00
commit 857f1268a5
28 changed files with 456 additions and 321 deletions

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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__ */

View File

@ -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:

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -2,3 +2,4 @@
arch/x86/lib/inat-tables.c
/objtool
fixdep
libsubcmd/

View File

@ -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)

View File

@ -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.

View File

@ -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:

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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"),

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -7,8 +7,6 @@
#include <subcmd/parse-options.h>
extern const struct option check_options[];
struct opts {
/* actions: */
bool dump_orc;

View File

@ -34,6 +34,7 @@ struct cfi_state {
unsigned char type;
bool bp_scratch;
bool drap;
bool signal;
bool end;
};

View File

@ -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 */

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;