Hi Sathvika, On Mon, Apr 22, 2024 at 02:52:06PM +0530, Sathvika Vasireddy wrote: > Implement build-time fixup of alternate feature relative addresses for > the out-of-line (else) patch code. Initial posting to achieve the same > using another tool can be found at [1]. Idea is to implement this using > objtool instead of introducing another tool since it already has elf > parsing and processing covered. > > Introduce --ftr-fixup as an option to objtool to do feature fixup at > build-time. > > Couple of issues and warnings encountered while implementing feature > fixup using objtool are as follows: > > 1. libelf is creating corrupted vmlinux file after writing necessary > changes to the file. Due to this, kexec is not able to load new > kernel. > > It gives the following error: > ELF Note corrupted ! > Cannot determine the file type of vmlinux > > To fix this issue, after opening vmlinux file, make a call to > elf_flagelf (e, ELF_C_SET, ELF_F_LAYOUT). This instructs libelf not > to touch the segment and section layout. It informs the library > that the application will take responsibility for the layout of the > file and that the library should not insert any padding between > sections. > > 2. Fix can't find starting instruction warnings when run on vmlinux > > Objtool throws a lot of can't find starting instruction warnings > when run on vmlinux with --ftr-fixup option. > > These warnings are seen because find_insn() function looks for > instructions at offsets that are relative to the start of the section. > In case of individual object files (.o), there are no can't find > starting instruction warnings seen because the actual offset > associated with an instruction is itself a relative offset since the > sections start at offset 0x0. > > However, in case of vmlinux, find_insn() function fails to find > instructions at the actual offset associated with an instruction > since the sections in vmlinux do not start at offset 0x0. Due to > this, find_insn() will look for absolute offset and not the relative > offset. This is resulting in a lot of can't find starting instruction > warnings when objtool is run on vmlinux. > > To fix this, pass offset that is relative to the start of the section > to find_insn(). > > find_insn() is also looking for symbols of size 0. But, objtool does > not store empty STT_NOTYPE symbols in the rbtree. Due to this, > for empty symbols, objtool is throwing can't find starting > instruction warnings. Fix this by ignoring symbols that are of > size 0 since objtool does not add them to the rbtree. > > 3. Objtool is throwing unannotated intra-function call warnings > when run on vmlinux with --ftr-fixup option. > > One such example: > > vmlinux: warning: objtool: .text+0x3d94: > unannotated intra-function call > > .text + 0x3d94 = c000000000008000 + 3d94 = c0000000000081d4 > > c0000000000081d4: 45 24 02 48 bl c00000000002a618 > <system_reset_exception+0x8> > > c00000000002a610 <system_reset_exception>: > c00000000002a610: 0e 01 4c 3c addis r2,r12,270 > c00000000002a610: R_PPC64_REL16_HA .TOC. > c00000000002a614: f0 6c 42 38 addi r2,r2,27888 > c00000000002a614: R_PPC64_REL16_LO .TOC.+0x4 > c00000000002a618: a6 02 08 7c mflr r0 > > This is happening because we should be looking for destination > symbols that are at absolute offsets instead of relative offsets. > After fixing dest_off to point to absolute offset, there are still > a lot of these warnings shown. > > In the above example, objtool is computing the destination > offset to be c00000000002a618, which points to a completely > different instruction. find_call_destination() is looking for this > offset and failing. Instead, we should be looking for destination > offset c00000000002a610 which points to system_reset_exception > function. > > Even after fixing the way destination offset is computed, and > after looking for dest_off - 0x8 in cases where the original offset > is not found, there are still a lot of unannotated intra-function > call warnings generated. This is due to symbols that are not > properly annotated. > > So, for now, as a hack to curb these warnings, do not emit > unannotated intra-function call warnings when objtool is run > with --ftr-fixup option. > > TODO: > This patch enables build time feature fixup only for powerpc little > endian configs. There are boot failures with big endian configs. > Posting this as an initial RFC to get some review comments while I work > on big endian issues. > > [1] > https://lore.kernel.org/linuxppc-dev/20170521010130.13552-1-npiggin@xxxxxxxxx/ > > Co-developed-by: Nicholas Piggin <npiggin@xxxxxxxxx> > Signed-off-by: Nicholas Piggin <npiggin@xxxxxxxxx> > Signed-off-by: Sathvika Vasireddy <sv@xxxxxxxxxxxxx> When I build this series with LLVM 14 [1] (due to an issue I report below), I am getting a crash when CONFIG_FTR_FIXUP_SELFTEST is disabled. diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 544a65fda77b..95d2906ec814 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -427,7 +427,6 @@ CONFIG_BLK_DEV_IO_TRACE=y CONFIG_IO_STRICT_DEVMEM=y CONFIG_PPC_EMULATED_STATS=y CONFIG_CODE_PATCHING_SELFTEST=y -CONFIG_FTR_FIXUP_SELFTEST=y CONFIG_MSI_BITMAP_SELFTEST=y CONFIG_XMON=y CONFIG_BOOTX_TEXT=y $ make -kj"$(nproc)" ARCH=powerpc LLVM=$PWD/llvm-14.0.6-x86_64/bin/ ppc64le_defconfig vmlinux ... LD vmlinux NM System.map SORTTAB vmlinux CHKHEAD vmlinux CHKREL vmlinux make[4]: *** [scripts/Makefile.vmlinux:38: vmlinux] Error 139 make[4]: *** Deleting file 'vmlinux ... I do not see the objtool command in V=1 output but I do see the end of scripts/link-vmlinux.sh, so it does seem like it is crashing in objtool. [1]: from https://mirrors.edge.kernel.org/pub/tools/llvm/ > --- > arch/Kconfig | 3 + > arch/powerpc/Kconfig | 5 + > arch/powerpc/Makefile | 5 + > arch/powerpc/include/asm/feature-fixups.h | 11 +- > arch/powerpc/kernel/vmlinux.lds.S | 14 +- > arch/powerpc/lib/feature-fixups.c | 13 + > scripts/Makefile.lib | 7 + > scripts/Makefile.vmlinux | 15 +- > tools/objtool/arch/powerpc/special.c | 329 ++++++++++++++++++++++ > tools/objtool/arch/x86/special.c | 49 ++++ > tools/objtool/builtin-check.c | 2 + > tools/objtool/check.c | 38 ++- > tools/objtool/elf.c | 4 + > tools/objtool/include/objtool/builtin.h | 1 + > tools/objtool/include/objtool/special.h | 43 +++ > 15 files changed, 530 insertions(+), 9 deletions(-) > > diff --git a/arch/Kconfig b/arch/Kconfig > index 9f066785bb71..8defdf86a69e 100644 > --- a/arch/Kconfig > +++ b/arch/Kconfig > @@ -1206,6 +1206,9 @@ config HAVE_UACCESS_VALIDATION > bool > select OBJTOOL > > +config HAVE_OBJTOOL_FTR_FIXUP > + bool > + > config HAVE_STACK_VALIDATION > bool > help > diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig > index 1c4be3373686..806285a28231 100644 > --- a/arch/powerpc/Kconfig > +++ b/arch/powerpc/Kconfig > @@ -23,6 +23,11 @@ config 64BIT > bool > default y if PPC64 > > +config HAVE_OBJTOOL_FTR_FIXUP > + bool > + default y if CPU_LITTLE_ENDIAN && PPC64 > + select OBJTOOL > + > config LIVEPATCH_64 > def_bool PPC64 > depends on LIVEPATCH > diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile > index 65261cbe5bfd..bc81847d5c3d 100644 > --- a/arch/powerpc/Makefile > +++ b/arch/powerpc/Makefile > @@ -112,6 +112,11 @@ LDFLAGS_vmlinux-$(CONFIG_RELOCATABLE) := -pie > LDFLAGS_vmlinux-$(CONFIG_RELOCATABLE) += -z notext > LDFLAGS_vmlinux := $(LDFLAGS_vmlinux-y) > > +# --emit-relocs required for post-link fixup of alternate feature > +# text section relocations. > +LDFLAGS_vmlinux += --emit-relocs > +KBUILD_LDFLAGS_MODULE += --emit-relocs > + > ifdef CONFIG_PPC64 > ifndef CONFIG_PPC_KERNEL_PCREL > ifeq ($(call cc-option-yn,-mcmodel=medium),y) > diff --git a/arch/powerpc/include/asm/feature-fixups.h b/arch/powerpc/include/asm/feature-fixups.h > index 77824bd289a3..006e2493c7c3 100644 > --- a/arch/powerpc/include/asm/feature-fixups.h > +++ b/arch/powerpc/include/asm/feature-fixups.h > @@ -30,12 +30,19 @@ > > #define START_FTR_SECTION(label) label##1: > > +#ifdef CONFIG_CPU_LITTLE_ENDIAN > #define FTR_SECTION_ELSE_NESTED(label) \ > label##2: \ > - .pushsection __ftr_alt_##label,"a"; \ > + .pushsection __ftr_alt_##label, "ax"; \ > .align 2; \ > label##3: > - > +#else > +#define FTR_SECTION_ELSE_NESTED(label) \ > +label##2 : \ > + .pushsection __ftr_alt_##label, "a"; \ > + .align 2; \ > +label##3 : > +#endif > > #ifndef CONFIG_CC_IS_CLANG > #define CHECK_ALT_SIZE(else_size, body_size) \ > diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S > index f420df7888a7..6b1c61e8af47 100644 > --- a/arch/powerpc/kernel/vmlinux.lds.S > +++ b/arch/powerpc/kernel/vmlinux.lds.S > @@ -105,8 +105,13 @@ SECTIONS > .text : AT(ADDR(.text) - LOAD_OFFSET) { > ALIGN_FUNCTION(); > #endif > - /* careful! __ftr_alt_* sections need to be close to .text */ > - *(.text.hot .text.hot.* TEXT_MAIN .text.fixup .text.unlikely .text.unlikely.* .fixup __ftr_alt_* .ref.text); > +#ifdef CONFIG_CPU_LITTLE_ENDIAN > + *(.text.hot .text.hot.* TEXT_MAIN .text.fixup .text.unlikely > + .text.unlikely.* .fixup .ref.text); > +#else > + *(.text.hot .text.hot.* TEXT_MAIN .text.fixup .text.unlikely > + .text.unlikely.* .fixup __ftr_alt_* .ref.text); > +#endif > *(.tramp.ftrace.text); > NOINSTR_TEXT > SCHED_TEXT > @@ -276,6 +281,11 @@ SECTIONS > _einittext = .; > *(.tramp.ftrace.init); > } :text > +#ifdef CONFIG_CPU_LITTLE_ENDIAN > + .__ftr_alternates.text : AT(ADDR(.__ftr_alternates.text) - LOAD_OFFSET) { > + *(__ftr_alt*); > + } > +#endif > > /* .exit.text is discarded at runtime, not link time, > * to deal with references from __bug_table > diff --git a/arch/powerpc/lib/feature-fixups.c b/arch/powerpc/lib/feature-fixups.c > index 4f82581ca203..8c5eb7c8612f 100644 > --- a/arch/powerpc/lib/feature-fixups.c > +++ b/arch/powerpc/lib/feature-fixups.c > @@ -44,6 +44,18 @@ static u32 *calc_addr(struct fixup_entry *fcur, long offset) > return (u32 *)((unsigned long)fcur + offset); > } > > +#ifdef CONFIG_CPU_LITTLE_ENDIAN > +static int patch_alt_instruction(u32 *src, u32 *dest, u32 *alt_start, u32 *alt_end) > +{ > + ppc_inst_t instr; > + > + instr = ppc_inst_read(src); > + > + raw_patch_instruction(dest, instr); > + > + return 0; > +} > +#else > static int patch_alt_instruction(u32 *src, u32 *dest, u32 *alt_start, u32 *alt_end) > { > int err; > @@ -66,6 +78,7 @@ static int patch_alt_instruction(u32 *src, u32 *dest, u32 *alt_start, u32 *alt_e > > return 0; > } > +#endif > > static int patch_feature_section_mask(unsigned long value, unsigned long mask, > struct fixup_entry *fcur) > diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib > index c65bb0fbd136..8fff27b9bdcb 100644 > --- a/scripts/Makefile.lib > +++ b/scripts/Makefile.lib > @@ -290,6 +290,13 @@ ifneq ($(objtool-args-y),) > cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool-args) $@) > endif > > +cmd_objtool_vmlinux := > +ifeq ($(CONFIG_HAVE_OBJTOOL_FTR_FIXUP),y) > +cmd_objtool_vmlinux = $(if $(objtool-enabled), ; $(objtool) $(objtool-args) $@) > +vmlinux: > + $(cmd_objtool_vmlinux) > +endif > + > cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd) > > endif # CONFIG_OBJTOOL > diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux > index c9f3e03124d7..2f4a7154e676 100644 > --- a/scripts/Makefile.vmlinux > +++ b/scripts/Makefile.vmlinux > @@ -30,7 +30,8 @@ ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink) > # Final link of vmlinux with optional arch pass after final link > cmd_link_vmlinux = \ > $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)"; \ > - $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true) > + $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true) \ > + $(cmd_objtool_vmlinux) > > targets += vmlinux > vmlinux: scripts/link-vmlinux.sh vmlinux.o $(KBUILD_LDS) FORCE > @@ -52,3 +53,15 @@ existing-targets := $(wildcard $(sort $(targets))) > -include $(foreach f,$(existing-targets),$(dir $(f)).$(notdir $(f)).cmd) > > .PHONY: $(PHONY) > + > +# objtool for vmlinux > +# ---------------------------------- > +# > +# For feature fixup, objtool does not run on individual > +# translation units. Run this on vmlinux instead. > + > +objtool-enabled := $(CONFIG_HAVE_OBJTOOL_FTR_FIXUP) > + > +vmlinux-objtool-args-$(CONFIG_HAVE_OBJTOOL_FTR_FIXUP) += --ftr-fixup > + > +objtool-args = $(vmlinux-objtool-args-y) --link > diff --git a/tools/objtool/arch/powerpc/special.c b/tools/objtool/arch/powerpc/special.c > index d33868147196..5ec3eed34eb0 100644 > --- a/tools/objtool/arch/powerpc/special.c > +++ b/tools/objtool/arch/powerpc/special.c > @@ -3,7 +3,17 @@ > #include <stdlib.h> > #include <objtool/special.h> > #include <objtool/builtin.h> > +#include <objtool/warn.h> > +#include <asm/byteorder.h> > +#include <errno.h> > > +struct section *ftr_alt; > + > +struct fixup_entry *fes; > +unsigned int nr_fes; > + > +uint64_t fe_alt_start = -1; > +uint64_t fe_alt_end; > > bool arch_support_alt_relocation(struct special_alt *special_alt, > struct instruction *insn, > @@ -17,3 +27,322 @@ struct reloc *arch_find_switch_table(struct objtool_file *file, > { > exit(-1); > } > + > +int process_alt_data(struct objtool_file *file) > +{ > + struct section *section; > + > + section = find_section_by_name(file->elf, ".__ftr_alternates.text"); > + ftr_alt = section; > + > + if (!ftr_alt) { > + WARN(".__ftr_alternates.text section not found\n"); > + return -1; > + } > + > + return 0; > +} > + > +int process_fixup_entries(struct objtool_file *file) > +{ > + struct section *sec; > + unsigned int nr = 0; > + int i; > + > + for_each_sec(file, sec) { > + if (strstr(sec->name, "_ftr_fixup") != NULL) { > + Elf_Data *data = sec->data; > + > + if (data && data->d_size > 0) > + nr = data->d_size / sizeof(struct fixup_entry); > + > + for (i = 0; i < nr; i++) { > + struct fixup_entry *dst; > + unsigned long idx; > + unsigned long long off; > + struct fixup_entry *src; > + > + idx = i * sizeof(struct fixup_entry); > + off = sec->sh.sh_addr + data->d_off + idx; > + src = data->d_buf + idx; > + > + if (src->alt_start_off == src->alt_end_off) > + continue; > + > + fes = realloc(fes, (nr_fes + 1) * sizeof(struct fixup_entry)); > + dst = &fes[nr_fes]; > + nr_fes++; > + > + dst->mask = __le64_to_cpu(src->mask); > + dst->value = __le64_to_cpu(src->value); > + dst->start_off = __le64_to_cpu(src->start_off) + off; > + dst->end_off = __le64_to_cpu(src->end_off) + off; > + dst->alt_start_off = __le64_to_cpu(src->alt_start_off) + off; > + dst->alt_end_off = __le64_to_cpu(src->alt_end_off) + off; > + > + if (dst->alt_start_off < fe_alt_start) > + fe_alt_start = dst->alt_start_off; > + > + if (dst->alt_end_off > fe_alt_end) > + fe_alt_end = dst->alt_end_off; > + } > + } > + } > + > + return 0; > +} > + > +struct fixup_entry *find_fe_altaddr(uint64_t addr) > +{ > + unsigned int i; > + > + if (addr < fe_alt_start) > + return NULL; > + if (addr >= fe_alt_end) > + return NULL; > + > + for (i = 0; i < nr_fes; i++) { > + if (addr >= fes[i].alt_start_off && addr < fes[i].alt_end_off) > + return &fes[i]; > + } > + return NULL; > +} > + > +int set_uncond_branch_target(uint32_t *insn, > + const uint64_t addr, uint64_t target) > +{ > + uint32_t i = *insn; > + int64_t offset; > + > + offset = target; > + if (!(i & BRANCH_ABSOLUTE)) > + offset = offset - addr; > + > + /* Check we can represent the target in the instruction format */ > + if (offset < -0x2000000 || offset > 0x1fffffc || offset & 0x3) > + return -EOVERFLOW; > + > + /* Mask out the flags and target, so they don't step on each other. */ > + *insn = 0x48000000 | (i & 0x3) | (offset & 0x03FFFFFC); > + > + return 0; > +} > + > +int set_cond_branch_target(uint32_t *insn, > + const uint64_t addr, uint64_t target) > +{ > + uint32_t i = *insn; > + int64_t offset; > + > + offset = target; > + > + if (!(i & BRANCH_ABSOLUTE)) > + offset = offset - addr; > + > + /* Check we can represent the target in the instruction format */ > + if (offset < -0x8000 || offset > 0x7FFF || offset & 0x3) { > + printf("cannot represent\n"); > + return -EOVERFLOW; > + } > + > + /* Mask out the flags and target, so they don't step on each other. */ > + *insn = 0x40000000 | (i & 0x3FF0003) | (offset & 0xFFFC); > + > + return 0; > +} > + > +void check_and_flatten_fixup_entries(void) > +{ > + static struct fixup_entry *fe; > + unsigned int i; > + > + i = nr_fes; > + while (i) { > + static struct fixup_entry *parent; > + uint64_t nested_off; /* offset from start of parent */ > + uint64_t size; > + > + i--; > + fe = &fes[i]; > + > + parent = find_fe_altaddr(fe->start_off); > + if (!parent) { > + parent = find_fe_altaddr(fe->end_off); > + continue; > + } > + > + size = fe->end_off - fe->start_off; > + nested_off = fe->start_off - parent->alt_start_off; > + > + fe->start_off = parent->start_off + nested_off; > + fe->end_off = fe->start_off + size; > + } > +} > + > +int process_bug_entries(struct objtool_file *file) > +{ > + struct section *section; > + > + Elf_Data *data; > + unsigned int nr, i; > + > + section = find_section_by_name(file->elf, "__bug_table"); > + > + data = section->data; > + > + nr = data->d_size / sizeof(struct bug_entry_64); > + > + for (i = 0; i < nr; i++) { > + unsigned long idx; > + uint64_t bugaddr; > + struct bug_entry_64 *bug; > + > + idx = i * sizeof(struct bug_entry_64); > + bug = data->d_buf + idx; > + bugaddr = __le64_to_cpu(bug->bug_addr); > + > + if (bugaddr < fe_alt_start) > + continue; > + > + if (bugaddr >= fe_alt_end) > + continue; > + } > + > + return 0; > +} > + > +static struct symbol *find_symbol_at_address_within_section(struct section *sec, > + unsigned long address) > +{ > + struct symbol *sym; > + > + sec_for_each_sym(sec, sym) { > + if (sym->sym.st_value <= address && address < sym->sym.st_value + sym->len) > + return sym; > + } > + > + return NULL; > +} > + > +static int is_local_symbol(uint8_t st_other) > +{ > + return (st_other & 0x3) != 0; > +} > + > +static struct symbol *find_symbol_at_address(struct objtool_file *file, > + unsigned long address) > +{ > + struct section *sec; > + struct symbol *sym; > + > + list_for_each_entry(sec, &file->elf->sections, list) { > + sym = find_symbol_at_address_within_section(sec, address); > + if (sym) > + return sym; > + } > + return NULL; > +} > + > +int process_alt_relocations(struct objtool_file *file) > +{ > + struct section *section; > + size_t n = 0; > + uint32_t insn; > + uint32_t *i; > + unsigned int opcode; > + > + section = find_section_by_name(file->elf, ".rela.__ftr_alternates.text"); > + if (!section) { > + printf(".rela.__ftr_alternates.text section not found.\n"); > + return -1; > + } > + > + for (int j = 0; j < sec_num_entries(section); j++) { > + struct reloc *relocation = §ion->relocs[j]; > + struct symbol *sym = relocation->sym; > + struct fixup_entry *fe; > + uint64_t addr = reloc_offset(relocation); > + uint64_t scn_delta; > + uint64_t dst_addr; > + const char *insn_ptr; > + unsigned long target = sym->sym.st_value + reloc_addend(relocation); > + > + struct symbol *symbol = find_symbol_at_address(file, target); > + > + if (symbol) { > + int is_local = is_local_symbol(symbol->sym.st_other); > + > + if (!is_local) > + target = target + 0x8; > + } > + > + n++; When HOSTCC is Clang 15 or newer, there is a warning (or error due to objtool using -Werror) that n is only incremented but never actually used: arch/powerpc/special.c:249:9: error: variable 'n' set but not used [-Werror,-Wunused-but-set-variable] 249 | size_t n = 0; | ^ 1 error generated. make[7]: *** [tools/build/Makefile.build:106: tools/objtool/arch/powerpc/special.o] Error 1 > + fe = find_fe_altaddr(addr); > + if (fe) { > + > + if (target >= fe->alt_start_off && > + target < fe->alt_end_off) > + continue; > + > + if (target >= ftr_alt->sh.sh_addr && > + target < ftr_alt->sh.sh_addr + > + ftr_alt->sh.sh_size) { > + printf("ftr_alt branch target is another ftr_alt region.\n"); > + exit(EXIT_FAILURE); > + } > + > + scn_delta = addr - ftr_alt->sh.sh_addr; > + dst_addr = addr - fe->alt_start_off + fe->start_off; > + > + i = ftr_alt->data->d_buf + scn_delta; > + insn = __le32_to_cpu(*i); > + > + opcode = insn >> 26; > + > + if (opcode == 16) > + set_cond_branch_target(&insn, dst_addr, target); > + > + if (opcode == 18) > + set_uncond_branch_target(&insn, dst_addr, target); > + > + insn_ptr = (const char *)&insn; > + elf_write_insn(file->elf, ftr_alt, scn_delta, sizeof(insn), insn_ptr); > + } > + } > + > + return 0; > +} > + > +int process_exception_entries(struct objtool_file *file) > +{ > + struct section *section; > + Elf_Data *data; > + unsigned int nr, i; > + > + section = find_section_by_name(file->elf, "__ex_table"); > + > + data = section->data; > + nr = data->d_size / sizeof(struct exception_entry_64); > + > + for (i = 0; i < nr; i++) { > + unsigned long idx; > + uint64_t exaddr; > + unsigned long long off; > + struct exception_entry_64 *ex; > + > + idx = i * sizeof(struct exception_entry_64); > + off = section->sh.sh_addr + data->d_off + idx; > + ex = data->d_buf + idx; > + exaddr = __le32_to_cpu(ex->insn) + off; > + > + if (exaddr < fe_alt_start) > + continue; > + if (exaddr >= fe_alt_end) > + continue; > + > + exit(EXIT_FAILURE); > + } > + > + return 0; > +} > diff --git a/tools/objtool/arch/x86/special.c b/tools/objtool/arch/x86/special.c > index 4134d27c696b..85527cf73e2d 100644 > --- a/tools/objtool/arch/x86/special.c > +++ b/tools/objtool/arch/x86/special.c > @@ -1,5 +1,6 @@ > // SPDX-License-Identifier: GPL-2.0-or-later > #include <string.h> > +#include <stdlib.h> > > #include <objtool/special.h> > #include <objtool/builtin.h> > @@ -137,3 +138,51 @@ struct reloc *arch_find_switch_table(struct objtool_file *file, > > return rodata_reloc; > } > + > + > +int process_alt_data(struct objtool_file *file) > +{ > + exit(-1); > +} > + > +int process_fixup_entries(struct objtool_file *file) > +{ > + exit(-1); > +} > + > +struct fixup_entry *find_fe_altaddr(uint64_t addr) > +{ > + exit(-1); > +} > + > +int set_uncond_branch_target(uint32_t *insn, > + const uint64_t addr, uint64_t target) > +{ > + exit(-1); > +} > + > +int set_cond_branch_target(uint32_t *insn, > + const uint64_t addr, uint64_t target) > +{ > + exit(-1); > +} > + > +void check_and_flatten_fixup_entries(void) > +{ > + exit(-1); > +} > + > +int process_bug_entries(struct objtool_file *file) > +{ > + exit(-1); > +} > + > +int process_alt_relocations(struct objtool_file *file) > +{ > + exit(-1); > +} > + > +int process_exception_entries(struct objtool_file *file) > +{ > + exit(-1); > +} > diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c > index 5e21cfb7661d..dae04ea2a9a4 100644 > --- a/tools/objtool/builtin-check.c > +++ b/tools/objtool/builtin-check.c > @@ -68,6 +68,7 @@ static int parse_hacks(const struct option *opt, const char *str, int unset) > 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('f', "ftr-fixup", &opts.ftr_fixup, "feature fixup"), > OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"), > OPT_BOOLEAN('m', "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"), > OPT_BOOLEAN('n', "noinstr", &opts.noinstr, "validate noinstr rules"), > @@ -132,6 +133,7 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[]) > static bool opts_valid(void) > { > if (opts.hack_jump_label || > + opts.ftr_fixup || > opts.hack_noinstr || > opts.ibt || > opts.mcount || > diff --git a/tools/objtool/check.c b/tools/objtool/check.c > index 0a33d9195b7a..d6a75fabefb6 100644 > --- a/tools/objtool/check.c > +++ b/tools/objtool/check.c > @@ -22,6 +22,9 @@ > #include <linux/static_call_types.h> > #include <linux/string.h> > > +#include <asm/byteorder.h> > +#include <errno.h> > + > struct alternative { > struct alternative *next; > struct instruction *insn; > @@ -456,12 +459,15 @@ static int decode_instructions(struct objtool_file *file) > return -1; > } > > + if (func->len == 0) > + continue; > + > if (func->embedded_insn || func->alias != func) > continue; > > - if (!find_insn(file, sec, func->offset)) { > - WARN("%s(): can't find starting instruction", > - func->name); > + if (!find_insn(file, sec, opts.ftr_fixup ? > + func->offset - sec->sym->offset : func->offset)) { > + WARN("%s(): can't find starting instruction", func->name); > return -1; > } > > @@ -1707,7 +1713,7 @@ static int add_call_destinations(struct objtool_file *file) > if (insn->ignore) > continue; > > - if (!insn_call_dest(insn)) { > + if (!insn_call_dest(insn) && !opts.ftr_fixup) { > WARN_INSN(insn, "unannotated intra-function call"); > return -1; > } > @@ -4720,6 +4726,30 @@ int check(struct objtool_file *file) > if (!nr_insns) > goto out; > > + if (opts.ftr_fixup) { > + ret = process_alt_data(file); > + if (ret < 0) > + return ret; > + > + ret = process_fixup_entries(file); > + if (ret < 0) > + return ret; > + > + check_and_flatten_fixup_entries(); > + > + ret = process_exception_entries(file); > + if (ret < 0) > + return ret; > + > + ret = process_bug_entries(file); > + if (ret < 0) > + return ret; > + > + ret = process_alt_relocations(file); > + if (ret < 0) > + return ret; > + } > + > if (opts.retpoline) { > ret = validate_retpoline(file); > if (ret < 0) > diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c > index 3d27983dc908..e5f8327f1d30 100644 > --- a/tools/objtool/elf.c > +++ b/tools/objtool/elf.c > @@ -1022,6 +1022,10 @@ struct elf *elf_open_read(const char *name, int flags) > cmd = ELF_C_WRITE; > > elf->elf = elf_begin(elf->fd, cmd, NULL); > + > + if (opts.ftr_fixup) > + elf_flagelf(elf->elf, ELF_C_SET, ELF_F_LAYOUT); > + > if (!elf->elf) { > WARN_ELF("elf_begin"); > goto err; > diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h > index fcca6662c8b4..2a80f206072c 100644 > --- a/tools/objtool/include/objtool/builtin.h > +++ b/tools/objtool/include/objtool/builtin.h > @@ -10,6 +10,7 @@ > struct opts { > /* actions: */ > bool dump_orc; > + bool ftr_fixup; > bool hack_jump_label; > bool hack_noinstr; > bool hack_skylake; > diff --git a/tools/objtool/include/objtool/special.h b/tools/objtool/include/objtool/special.h > index 86d4af9c5aa9..390a0e7816b7 100644 > --- a/tools/objtool/include/objtool/special.h > +++ b/tools/objtool/include/objtool/special.h > @@ -12,6 +12,28 @@ > > #define C_JUMP_TABLE_SECTION ".rodata..c_jump_table" > > +#define BRANCH_SET_LINK 0x1 > +#define BRANCH_ABSOLUTE 0x2 > + > +struct bug_entry_64 { > + uint64_t bug_addr; > + uint16_t flags; > +}; > + > +struct exception_entry_64 { > + int32_t insn; > + int32_t fixup; > +}; > + > +struct fixup_entry { > + uint64_t mask; > + uint64_t value; > + uint64_t start_off; > + uint64_t end_off; > + uint64_t alt_start_off; > + uint64_t alt_end_off; > +}; > + > struct special_alt { > struct list_head list; > > @@ -37,6 +59,27 @@ void arch_handle_alternative(unsigned short feature, struct special_alt *alt); > bool arch_support_alt_relocation(struct special_alt *special_alt, > struct instruction *insn, > struct reloc *reloc); > + > struct reloc *arch_find_switch_table(struct objtool_file *file, > struct instruction *insn); > + > +int process_alt_data(struct objtool_file *file); > + > +int process_fixup_entries(struct objtool_file *file); > + > +void check_and_flatten_fixup_entries(void); > + > +int process_exception_entries(struct objtool_file *file); > + > +int process_bug_entries(struct objtool_file *file); > + > +int process_alt_relocations(struct objtool_file *file); > + > +struct fixup_entry *find_fe_altaddr(uint64_t addr); > + > +int set_uncond_branch_target(uint32_t *insn, > + const uint64_t addr, uint64_t target); > + > +int set_cond_branch_target(uint32_t *insn, > + const uint64_t addr, uint64_t target); > #endif /* _SPECIAL_H */ > -- > 2.34.1 >