diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-03-02 09:45:34 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-03-02 09:45:34 -0800 |
| commit | 857f1268a591147f7be7509f249dbb3aba6fc65c (patch) | |
| tree | 90ba64cb2eee5ee7226ab18c80b43a348fb420a5 /tools | |
| parent | 6972633c58fd13c02dcaabcb6be380a98feda9fa (diff) | |
| parent | 00c8f01c4e84637c3db76f368b8687cb61f4dd9d (diff) | |
| download | linux-857f1268a591147f7be7509f249dbb3aba6fc65c.tar.gz linux-857f1268a591147f7be7509f249dbb3aba6fc65c.tar.bz2 linux-857f1268a591147f7be7509f249dbb3aba6fc65c.zip | |
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
...
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/arch/x86/include/asm/orc_types.h | 4 | ||||
| -rw-r--r-- | tools/include/linux/objtool.h | 11 | ||||
| -rw-r--r-- | tools/objtool/.gitignore | 1 | ||||
| -rw-r--r-- | tools/objtool/Build | 2 | ||||
| -rw-r--r-- | tools/objtool/Documentation/objtool.txt | 8 | ||||
| -rw-r--r-- | tools/objtool/Makefile | 66 | ||||
| -rw-r--r-- | tools/objtool/arch/powerpc/decode.c | 22 | ||||
| -rw-r--r-- | tools/objtool/arch/x86/decode.c | 107 | ||||
| -rw-r--r-- | tools/objtool/builtin-check.c | 2 | ||||
| -rw-r--r-- | tools/objtool/check.c | 375 | ||||
| -rw-r--r-- | tools/objtool/elf.c | 42 | ||||
| -rw-r--r-- | tools/objtool/include/objtool/arch.h | 6 | ||||
| -rw-r--r-- | tools/objtool/include/objtool/builtin.h | 2 | ||||
| -rw-r--r-- | tools/objtool/include/objtool/cfi.h | 1 | ||||
| -rw-r--r-- | tools/objtool/include/objtool/check.h | 61 | ||||
| -rw-r--r-- | tools/objtool/include/objtool/elf.h | 9 | ||||
| -rw-r--r-- | tools/objtool/include/objtool/objtool.h | 1 | ||||
| -rw-r--r-- | tools/objtool/include/objtool/special.h | 2 | ||||
| -rw-r--r-- | tools/objtool/objtool.c | 1 | ||||
| -rw-r--r-- | tools/objtool/orc_dump.c | 4 | ||||
| -rw-r--r-- | tools/objtool/orc_gen.c | 1 | ||||
| -rw-r--r-- | tools/objtool/special.c | 6 |
22 files changed, 429 insertions, 305 deletions
diff --git a/tools/arch/x86/include/asm/orc_types.h b/tools/arch/x86/include/asm/orc_types.h index 5a2baf28a1dc..1343a62106de 100644 --- a/tools/arch/x86/include/asm/orc_types.h +++ b/tools/arch/x86/include/asm/orc_types.h @@ -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; diff --git a/tools/include/linux/objtool.h b/tools/include/linux/objtool.h index 62c54ffbeeaa..9ac3df3fccf0 100644 --- a/tools/include/linux/objtool.h +++ b/tools/include/linux/objtool.h @@ -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 diff --git a/tools/objtool/.gitignore b/tools/objtool/.gitignore index 14236db3677f..4faa4dd72f35 100644 --- a/tools/objtool/.gitignore +++ b/tools/objtool/.gitignore @@ -2,3 +2,4 @@ arch/x86/lib/inat-tables.c /objtool fixdep +libsubcmd/ diff --git a/tools/objtool/Build b/tools/objtool/Build index 33f2ee5a46d3..a3cdf8af6635 100644 --- a/tools/objtool/Build +++ b/tools/objtool/Build @@ -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) diff --git a/tools/objtool/Documentation/objtool.txt b/tools/objtool/Documentation/objtool.txt index 8a671902a187..8e53fc6735ef 100644 --- a/tools/objtool/Documentation/objtool.txt +++ b/tools/objtool/Documentation/objtool.txt @@ -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. diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile index a3a9cc24e0e3..83b100c1e7f6 100644 --- a/tools/objtool/Makefile +++ b/tools/objtool/Makefile @@ -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_OUTPUT): + $(Q)$(MKDIR) -p $@ +$(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): fixdep FORCE - $(Q)$(MAKE) -C $(SUBCMD_SRCDIR) OUTPUT=$(LIBSUBCMD_OUTPUT) +$(LIBSUBCMD)-clean: + $(call QUIET_CLEAN, libsubcmd) + $(Q)$(RM) -r -- $(LIBSUBCMD_OUTPUT) -clean: +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: diff --git a/tools/objtool/arch/powerpc/decode.c b/tools/objtool/arch/powerpc/decode.c index 9c653805a08a..53b55690f320 100644 --- a/tools/objtool/arch/powerpc/decode.c +++ b/tools/objtool/arch/powerpc/decode.c @@ -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; } diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index e7b030f7e2a5..9ef024fd648c 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -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; } diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index a4f39407bf59..7c175198d09f 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -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"), diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 35374812afdc..f937be1afe65 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -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_sec(struct objtool_file *file, + struct instruction *insn) +{ + if (insn->idx == 0) { + if (insn->prev_len) + return find_insn(file, insn->sec, insn->offset - insn->prev_len); + return NULL; + } + + return insn - 1; +} + static struct instruction *prev_insn_same_sym(struct objtool_file *file, - struct instruction *insn) + struct instruction *insn) { - struct instruction *prev = list_prev_entry(insn, list); + struct instruction *prev = prev_insn_same_sec(file, insn); - if (&prev->list != &file->insn_list && insn_func(prev) == insn_func(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 - off |
