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-npig...@gmail.com/
> 
> Co-developed-by: Nicholas Piggin <npig...@gmail.com>
> Signed-off-by: Nicholas Piggin <npig...@gmail.com>
> Signed-off-by: Sathvika Vasireddy <s...@linux.ibm.com>

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 = &section->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
> 

Reply via email to