]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
Merge tag 'loongarch-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 22 Mar 2024 17:22:45 +0000 (10:22 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 22 Mar 2024 17:22:45 +0000 (10:22 -0700)
Pull LoongArch updates from Huacai Chen:

 - Add objtool support for LoongArch

 - Add ORC stack unwinder support for LoongArch

 - Add kernel livepatching support for LoongArch

 - Select ARCH_HAS_CURRENT_STACK_POINTER in Kconfig

 - Select HAVE_ARCH_USERFAULTFD_MINOR in Kconfig

 - Some bug fixes and other small changes

* tag 'loongarch-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson:
  LoongArch/crypto: Clean up useless assignment operations
  LoongArch: Define the __io_aw() hook as mmiowb()
  LoongArch: Remove superfluous flush_dcache_page() definition
  LoongArch: Move {dmw,tlb}_virt_to_page() definition to page.h
  LoongArch: Change __my_cpu_offset definition to avoid mis-optimization
  LoongArch: Select HAVE_ARCH_USERFAULTFD_MINOR in Kconfig
  LoongArch: Select ARCH_HAS_CURRENT_STACK_POINTER in Kconfig
  LoongArch: Add kernel livepatching support
  LoongArch: Add ORC stack unwinder support
  objtool: Check local label in read_unwind_hints()
  objtool: Check local label in add_dead_ends()
  objtool/LoongArch: Enable orc to be built
  objtool/x86: Separate arch-specific and generic parts
  objtool/LoongArch: Implement instruction decoder
  objtool/LoongArch: Enable objtool to be built

1  2 
arch/loongarch/Kconfig
arch/loongarch/Makefile
arch/loongarch/include/asm/page.h
arch/loongarch/kernel/setup.c
arch/loongarch/kvm/switch.S
include/linux/compiler.h
scripts/Makefile
tools/objtool/check.c

diff --combined arch/loongarch/Kconfig
index c139d0d728029e200899c857c9cbb50e45c9d7c2,526a88598fbfc514c8b3be295d9eb20b6fccadca..a5f300ec6f2808b8890ebc27d0de8a918eaa8636
@@@ -15,6 -15,7 +15,7 @@@ config LOONGARC
        select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE
        select ARCH_HAS_ACPI_TABLE_UPGRADE      if ACPI
        select ARCH_HAS_CPU_FINALIZE_INIT
+       select ARCH_HAS_CURRENT_STACK_POINTER
        select ARCH_HAS_FORTIFY_SOURCE
        select ARCH_HAS_KCOV
        select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS
        select HAVE_ARCH_SECCOMP_FILTER
        select HAVE_ARCH_TRACEHOOK
        select HAVE_ARCH_TRANSPARENT_HUGEPAGE
+       select HAVE_ARCH_USERFAULTFD_MINOR if USERFAULTFD
        select HAVE_ASM_MODVERSIONS
        select HAVE_CONTEXT_TRACKING_USER
        select HAVE_C_RECORDMCOUNT
        select HAVE_KPROBES
        select HAVE_KPROBES_ON_FTRACE
        select HAVE_KRETPROBES
 -      select HAVE_KVM
+       select HAVE_LIVEPATCH
        select HAVE_MOD_ARCH_SPECIFIC
        select HAVE_NMI
+       select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS
        select HAVE_PCI
        select HAVE_PERF_EVENTS
        select HAVE_PERF_REGS
        select HAVE_PERF_USER_STACK_DUMP
        select HAVE_PREEMPT_DYNAMIC_KEY
        select HAVE_REGS_AND_STACK_ACCESS_API
+       select HAVE_RELIABLE_STACKTRACE if UNWINDER_ORC
        select HAVE_RETHOOK
        select HAVE_RSEQ
        select HAVE_RUST
        select HAVE_SAMPLE_FTRACE_DIRECT
        select HAVE_SAMPLE_FTRACE_DIRECT_MULTI
        select HAVE_SETUP_PER_CPU_AREA if NUMA
+       select HAVE_STACK_VALIDATION if HAVE_OBJTOOL
        select HAVE_STACKPROTECTOR
        select HAVE_SYSCALL_TRACEPOINTS
        select HAVE_TIF_NOHZ
@@@ -226,6 -233,15 +232,6 @@@ config MACH_LOONGSON6
  config FIX_EARLYCON_MEM
        def_bool y
  
 -config PAGE_SIZE_4KB
 -      bool
 -
 -config PAGE_SIZE_16KB
 -      bool
 -
 -config PAGE_SIZE_64KB
 -      bool
 -
  config PGTABLE_2LEVEL
        bool
  
@@@ -278,7 -294,7 +284,7 @@@ choic
  
  config 4KB_3LEVEL
        bool "4KB with 3 levels"
 -      select PAGE_SIZE_4KB
 +      select HAVE_PAGE_SIZE_4KB
        select PGTABLE_3LEVEL
        help
          This option selects 4KB page size with 3 level page tables, which
  
  config 4KB_4LEVEL
        bool "4KB with 4 levels"
 -      select PAGE_SIZE_4KB
 +      select HAVE_PAGE_SIZE_4KB
        select PGTABLE_4LEVEL
        help
          This option selects 4KB page size with 4 level page tables, which
  
  config 16KB_2LEVEL
        bool "16KB with 2 levels"
 -      select PAGE_SIZE_16KB
 +      select HAVE_PAGE_SIZE_16KB
        select PGTABLE_2LEVEL
        help
          This option selects 16KB page size with 2 level page tables, which
  
  config 16KB_3LEVEL
        bool "16KB with 3 levels"
 -      select PAGE_SIZE_16KB
 +      select HAVE_PAGE_SIZE_16KB
        select PGTABLE_3LEVEL
        help
          This option selects 16KB page size with 3 level page tables, which
  
  config 64KB_2LEVEL
        bool "64KB with 2 levels"
 -      select PAGE_SIZE_64KB
 +      select HAVE_PAGE_SIZE_64KB
        select PGTABLE_2LEVEL
        help
          This option selects 64KB page size with 2 level page tables, which
  
  config 64KB_3LEVEL
        bool "64KB with 3 levels"
 -      select PAGE_SIZE_64KB
 +      select HAVE_PAGE_SIZE_64KB
        select PGTABLE_3LEVEL
        help
          This option selects 64KB page size with 3 level page tables, which
@@@ -624,6 -640,8 +630,8 @@@ config RANDOMIZE_BASE_MAX_OFFSE
  
          This is limited by the size of the lower address memory, 256MB.
  
+ source "kernel/livepatch/Kconfig"
  endmenu
  
  config ARCH_SELECT_MEMORY_MODEL
diff --combined arch/loongarch/Makefile
index fa4fb09909aeb1c7ebe640329936cd095e8224c8,e3bc02fb7fdcce1d3224371b73c0578adb7dd827..df6caf79537a9d884aa97161a32840782147460e
@@@ -26,6 -26,18 +26,18 @@@ endi
  32bit-emul            = elf32loongarch
  64bit-emul            = elf64loongarch
  
+ ifdef CONFIG_UNWINDER_ORC
+ orc_hash_h := arch/$(SRCARCH)/include/generated/asm/orc_hash.h
+ orc_hash_sh := $(srctree)/scripts/orc_hash.sh
+ targets += $(orc_hash_h)
+ quiet_cmd_orc_hash = GEN     $@
+       cmd_orc_hash = mkdir -p $(dir $@); \
+                    $(CONFIG_SHELL) $(orc_hash_sh) < $< > $@
+ $(orc_hash_h): $(srctree)/arch/loongarch/include/asm/orc_types.h $(orc_hash_sh) FORCE
+       $(call if_changed,orc_hash)
+ archprepare: $(orc_hash_h)
+ endif
  ifdef CONFIG_DYNAMIC_FTRACE
  KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
  CC_FLAGS_FTRACE := -fpatchable-function-entry=2
@@@ -72,8 -84,6 +84,6 @@@ KBUILD_CFLAGS_KERNEL          += $(call cc-opti
  KBUILD_CFLAGS_KERNEL          += $(call cc-option,-fdirect-access-external-data)
  KBUILD_AFLAGS_MODULE          += $(call cc-option,-fno-direct-access-external-data)
  KBUILD_CFLAGS_MODULE          += $(call cc-option,-fno-direct-access-external-data)
- KBUILD_AFLAGS_MODULE          += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax)
- KBUILD_CFLAGS_MODULE          += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax)
  else
  cflags-y                      += $(call cc-option,-mno-explicit-relocs)
  KBUILD_AFLAGS_KERNEL          += -Wa,-mla-global-with-pcrel
@@@ -82,7 -92,15 +92,16 @@@ KBUILD_AFLAGS_MODULE                += -Wa,-mla-globa
  KBUILD_CFLAGS_MODULE          += -fplt -Wa,-mla-global-with-abs,-mla-local-with-abs
  endif
  
+ KBUILD_AFLAGS                 += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax)
+ KBUILD_CFLAGS                 += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax)
+ KBUILD_AFLAGS                 += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma)-mthin-add-sub)
+ KBUILD_CFLAGS                 += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma)-mthin-add-sub)
+ ifdef CONFIG_OBJTOOL
+ KBUILD_CFLAGS                 += -fno-jump-tables
+ endif
 +KBUILD_RUSTFLAGS                      += --target=$(objtree)/scripts/target.json
  KBUILD_RUSTFLAGS_MODULE               += -Crelocation-model=pic
  
  ifeq ($(CONFIG_RELOCATABLE),y)
index afb6fa16b82636caed29820a118a895f7446ad72,2391fcd189087a2d70c5d6ebe512471f7e84a17e..44027060c54a28bd34a80f538135491e3ebc758a
  /*
   * PAGE_SHIFT determines the page size
   */
 -#ifdef CONFIG_PAGE_SIZE_4KB
 -#define PAGE_SHIFT    12
 -#endif
 -#ifdef CONFIG_PAGE_SIZE_16KB
 -#define PAGE_SHIFT    14
 -#endif
 -#ifdef CONFIG_PAGE_SIZE_64KB
 -#define PAGE_SHIFT    16
 -#endif
 +#define PAGE_SHIFT    CONFIG_PAGE_SHIFT
  #define PAGE_SIZE     (_AC(1, UL) << PAGE_SHIFT)
  #define PAGE_MASK     (~(PAGE_SIZE - 1))
  
@@@ -75,6 -83,9 +75,9 @@@ typedef struct { unsigned long pgprot; 
  #define pfn_to_kaddr(pfn)     __va((pfn) << PAGE_SHIFT)
  #define sym_to_pfn(x)         __phys_to_pfn(__pa_symbol(x))
  
+ struct page *dmw_virt_to_page(unsigned long kaddr);
+ struct page *tlb_virt_to_page(unsigned long kaddr);
  #define virt_to_pfn(kaddr)    PFN_DOWN(PHYSADDR(kaddr))
  
  #define virt_to_page(kaddr)                                                           \
index 2b72eb326b44288a7ee4aba76e69c25bd75789d4,7bf9afaeea006b2be3201e1903b97a5552e3f39e..60e0fe97f61a31604b095044395c40ba6fd5efc8
@@@ -47,6 -47,7 +47,7 @@@
  #include <asm/sections.h>
  #include <asm/setup.h>
  #include <asm/time.h>
+ #include <asm/unwind.h>
  
  #define SMBIOS_BIOSSIZE_OFFSET                0x09
  #define SMBIOS_BIOSEXTERN_OFFSET      0x13
@@@ -260,7 -261,7 +261,7 @@@ static void __init arch_reserve_crashke
        char *cmdline = boot_command_line;
        bool high = false;
  
 -      if (!IS_ENABLED(CONFIG_KEXEC_CORE))
 +      if (!IS_ENABLED(CONFIG_CRASH_RESERVE))
                return;
  
        ret = parse_crashkernel(cmdline, memblock_phys_mem_size(),
@@@ -490,7 -491,7 +491,7 @@@ static int __init add_legacy_isa_io(str
        }
  
        vaddr = (unsigned long)(PCI_IOBASE + range->io_start);
 -      ioremap_page_range(vaddr, vaddr + size, hw_start, pgprot_device(PAGE_KERNEL));
 +      vmap_page_range(vaddr, vaddr + size, hw_start, pgprot_device(PAGE_KERNEL));
  
        return 0;
  }
@@@ -587,6 -588,7 +588,7 @@@ static void __init prefill_possible_map
  void __init setup_arch(char **cmdline_p)
  {
        cpu_probe();
+       unwind_init();
  
        init_environ();
        efi_init();
index 3634431db18a4a4992ea9b7d1ed44545214ff3df,1fcc4b7eda32ae1003f76974ada1f8c044a4820d..80e988985a6adfa97cce7e92d1ebdef4b0cc1ca4
@@@ -8,7 -8,7 +8,7 @@@
  #include <asm/asmmacro.h>
  #include <asm/loongarch.h>
  #include <asm/regdef.h>
- #include <asm/stackframe.h>
+ #include <asm/unwind_hints.h>
  
  #define HGPR_OFFSET(x)                (PT_R0 + 8*x)
  #define GGPR_OFFSET(x)                (KVM_ARCH_GGPR + 8*x)
        .text
        .cfi_sections   .debug_frame
  SYM_CODE_START(kvm_exc_entry)
+       UNWIND_HINT_UNDEFINED
        csrwr   a2,   KVM_TEMP_KS
        csrrd   a2,   KVM_VCPU_KS
        addi.d  a2,   a2, KVM_VCPU_ARCH
@@@ -213,6 -214,12 +214,6 @@@ SYM_FUNC_START(kvm_enter_guest
        /* Save host GPRs */
        kvm_save_host_gpr a2
  
 -      /* Save host CRMD, PRMD to stack */
 -      csrrd   a3, LOONGARCH_CSR_CRMD
 -      st.d    a3, a2, PT_CRMD
 -      csrrd   a3, LOONGARCH_CSR_PRMD
 -      st.d    a3, a2, PT_PRMD
 -
        addi.d  a2, a1, KVM_VCPU_ARCH
        st.d    sp, a2, KVM_ARCH_HSP
        st.d    tp, a2, KVM_ARCH_HTP
@@@ -273,3 -280,9 +274,9 @@@ SYM_FUNC_END(kvm_restore_lasx
        .section ".rodata"
  SYM_DATA(kvm_exception_size, .quad kvm_exc_entry_end - kvm_exc_entry)
  SYM_DATA(kvm_enter_guest_size, .quad kvm_enter_guest_end - kvm_enter_guest)
+ #ifdef CONFIG_CPU_HAS_LBT
+ STACK_FRAME_NON_STANDARD kvm_restore_fpu
+ STACK_FRAME_NON_STANDARD kvm_restore_lsx
+ STACK_FRAME_NON_STANDARD kvm_restore_lasx
+ #endif
diff --combined include/linux/compiler.h
index 52730e4236811b3949d02d1a8c0bf4328a1e6c52,39f2d4a05208db89e25033f8cb64578ac1b26757..c00cc6c0878a1e173701a6267ac062fa3d41b790
@@@ -116,6 -116,14 +116,14 @@@ void ftrace_likely_update(struct ftrace
   */
  #define __stringify_label(n) #n
  
+ #define __annotate_reachable(c) ({                                    \
+       asm volatile(__stringify_label(c) ":\n\t"                       \
+                       ".pushsection .discard.reachable\n\t"           \
+                       ".long " __stringify_label(c) "b - .\n\t"       \
+                       ".popsection\n\t");                             \
+ })
+ #define annotate_reachable() __annotate_reachable(__COUNTER__)
  #define __annotate_unreachable(c) ({                                  \
        asm volatile(__stringify_label(c) ":\n\t"                       \
                     ".pushsection .discard.unreachable\n\t"            \
  #define __annotate_jump_table __section(".rodata..c_jump_table")
  
  #else /* !CONFIG_OBJTOOL */
+ #define annotate_reachable()
  #define annotate_unreachable()
  #define __annotate_jump_table
  #endif /* CONFIG_OBJTOOL */
   */
  #define ___ADDRESSABLE(sym, __attrs) \
        static void * __used __attrs \
 -              __UNIQUE_ID(__PASTE(__addressable_,sym)) = (void *)&sym;
 +      __UNIQUE_ID(__PASTE(__addressable_,sym)) = (void *)(uintptr_t)&sym;
  #define __ADDRESSABLE(sym) \
        ___ADDRESSABLE(sym, __section(".discard.addressable"))
  
@@@ -231,45 -240,6 +240,45 @@@ static inline void *offset_to_ptr(cons
   * This returns a constant expression while determining if an argument is
   * a constant expression, most importantly without evaluating the argument.
   * Glory to Martin Uecker <Martin.Uecker@med.uni-goettingen.de>
 + *
 + * Details:
 + * - sizeof() return an integer constant expression, and does not evaluate
 + *   the value of its operand; it only examines the type of its operand.
 + * - The results of comparing two integer constant expressions is also
 + *   an integer constant expression.
 + * - The first literal "8" isn't important. It could be any literal value.
 + * - The second literal "8" is to avoid warnings about unaligned pointers;
 + *   this could otherwise just be "1".
 + * - (long)(x) is used to avoid warnings about 64-bit types on 32-bit
 + *   architectures.
 + * - The C Standard defines "null pointer constant", "(void *)0", as
 + *   distinct from other void pointers.
 + * - If (x) is an integer constant expression, then the "* 0l" resolves
 + *   it into an integer constant expression of value 0. Since it is cast to
 + *   "void *", this makes the second operand a null pointer constant.
 + * - If (x) is not an integer constant expression, then the second operand
 + *   resolves to a void pointer (but not a null pointer constant: the value
 + *   is not an integer constant 0).
 + * - The conditional operator's third operand, "(int *)8", is an object
 + *   pointer (to type "int").
 + * - The behavior (including the return type) of the conditional operator
 + *   ("operand1 ? operand2 : operand3") depends on the kind of expressions
 + *   given for the second and third operands. This is the central mechanism
 + *   of the macro:
 + *   - When one operand is a null pointer constant (i.e. when x is an integer
 + *     constant expression) and the other is an object pointer (i.e. our
 + *     third operand), the conditional operator returns the type of the
 + *     object pointer operand (i.e. "int *). Here, within the sizeof(), we
 + *     would then get:
 + *       sizeof(*((int *)(...))  == sizeof(int)  == 4
 + *   - When one operand is a void pointer (i.e. when x is not an integer
 + *     constant expression) and the other is an object pointer (i.e. our
 + *     third operand), the conditional operator returns a "void *" type.
 + *     Here, within the sizeof(), we would then get:
 + *       sizeof(*((void *)(...)) == sizeof(void) == 1
 + * - The equality comparison to "sizeof(int)" therefore depends on (x):
 + *     sizeof(int) == sizeof(int)     (x) was a constant expression
 + *     sizeof(int) != sizeof(void)    (x) was not a constant expression
   */
  #define __is_constexpr(x) \
        (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
diff --combined scripts/Makefile
index 6673cbb6194fe0b317014b7a96485d78d87fe19b,e4cca53d22858bfac9e12cdf4fc68f9bfff331f7..bc90520a54266cc1d9cd2584ccde4e874f3827a7
@@@ -11,14 -11,12 +11,14 @@@ hostprogs-always-$(CONFIG_MODULE_SIG_FO
  hostprogs-always-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE)   += insert-sys-cert
  hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS)               += rustdoc_test_builder
  hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS)               += rustdoc_test_gen
 -always-$(CONFIG_RUST)                                 += target.json
  
 +ifneq ($(or $(CONFIG_X86_64),$(CONFIG_LOONGARCH)),)
 +always-$(CONFIG_RUST)                                 += target.json
  filechk_rust_target = $< < include/config/auto.conf
  
  $(obj)/target.json: scripts/generate_rust_target include/config/auto.conf FORCE
        $(call filechk,rust_target)
 +endif
  
  hostprogs += generate_rust_target
  generate_rust_target-rust := y
@@@ -33,9 -31,12 +33,12 @@@ HOSTLDLIBS_sign-file = $(shell $(HOSTPK
  
  ifdef CONFIG_UNWINDER_ORC
  ifeq ($(ARCH),x86_64)
- ARCH := x86
SRCARCH := x86
  endif
- HOSTCFLAGS_sorttable.o += -I$(srctree)/tools/arch/x86/include
+ ifeq ($(ARCH),loongarch)
+ SRCARCH := loongarch
+ endif
+ HOSTCFLAGS_sorttable.o += -I$(srctree)/tools/arch/$(SRCARCH)/include
  HOSTCFLAGS_sorttable.o += -DUNWINDER_ORC_ENABLED
  endif
  
diff --combined tools/objtool/check.c
index eb7e12ebc1d06afba3ddb9544ef8962f794ddd30,1009d144f756adec4795a75ea6f631636b7e8a5c..0b10ad00866867d9f4ba6787ca000f43bed230be
@@@ -20,6 -20,7 +20,7 @@@
  #include <linux/hashtable.h>
  #include <linux/kernel.h>
  #include <linux/static_call_types.h>
+ #include <linux/string.h>
  
  struct alternative {
        struct alternative *next;
@@@ -584,7 -585,7 +585,7 @@@ static int add_dead_ends(struct objtool
        struct section *rsec;
        struct reloc *reloc;
        struct instruction *insn;
-       s64 addend;
+       unsigned long offset;
  
        /*
         * Check for manually annotated dead ends.
                goto reachable;
  
        for_each_reloc(rsec, reloc) {
-               if (reloc->sym->type != STT_SECTION) {
+               if (reloc->sym->type == STT_SECTION) {
+                       offset = reloc_addend(reloc);
+               } else if (reloc->sym->local_label) {
+                       offset = reloc->sym->offset;
+               } else {
                        WARN("unexpected relocation symbol type in %s", rsec->name);
                        return -1;
                }
  
-               addend = reloc_addend(reloc);
-               insn = find_insn(file, reloc->sym->sec, addend);
+               insn = find_insn(file, reloc->sym->sec, offset);
                if (insn)
                        insn = prev_insn_same_sec(file, insn);
-               else if (addend == reloc->sym->sec->sh.sh_size) {
+               else if (offset == reloc->sym->sec->sh.sh_size) {
                        insn = find_last_insn(file, reloc->sym->sec);
                        if (!insn) {
                                WARN("can't find unreachable insn at %s+0x%" PRIx64,
-                                    reloc->sym->sec->name, addend);
+                                    reloc->sym->sec->name, offset);
                                return -1;
                        }
                } else {
                        WARN("can't find unreachable insn at %s+0x%" PRIx64,
-                            reloc->sym->sec->name, addend);
+                            reloc->sym->sec->name, offset);
                        return -1;
                }
  
@@@ -633,27 -635,28 +635,28 @@@ reachable
                return 0;
  
        for_each_reloc(rsec, reloc) {
-               if (reloc->sym->type != STT_SECTION) {
+               if (reloc->sym->type == STT_SECTION) {
+                       offset = reloc_addend(reloc);
+               } else if (reloc->sym->local_label) {
+                       offset = reloc->sym->offset;
+               } else {
                        WARN("unexpected relocation symbol type in %s", rsec->name);
                        return -1;
                }
  
-               addend = reloc_addend(reloc);
-               insn = find_insn(file, reloc->sym->sec, addend);
+               insn = find_insn(file, reloc->sym->sec, offset);
                if (insn)
                        insn = prev_insn_same_sec(file, insn);
-               else if (addend == reloc->sym->sec->sh.sh_size) {
+               else if (offset == reloc->sym->sec->sh.sh_size) {
                        insn = find_last_insn(file, reloc->sym->sec);
                        if (!insn) {
                                WARN("can't find reachable insn at %s+0x%" PRIx64,
-                                    reloc->sym->sec->name, addend);
+                                    reloc->sym->sec->name, offset);
                                return -1;
                        }
                } else {
                        WARN("can't find reachable insn at %s+0x%" PRIx64,
-                            reloc->sym->sec->name, addend);
+                            reloc->sym->sec->name, offset);
                        return -1;
                }
  
@@@ -2224,6 -2227,7 +2227,7 @@@ static int read_unwind_hints(struct obj
        struct unwind_hint *hint;
        struct instruction *insn;
        struct reloc *reloc;
+       unsigned long offset;
        int i;
  
        sec = find_section_by_name(file->elf, ".discard.unwind_hints");
                        return -1;
                }
  
-               insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
+               if (reloc->sym->type == STT_SECTION) {
+                       offset = reloc_addend(reloc);
+               } else if (reloc->sym->local_label) {
+                       offset = reloc->sym->offset;
+               } else {
+                       WARN("unexpected relocation symbol type in %s", sec->rsec->name);
+                       return -1;
+               }
+               insn = find_insn(file, reloc->sym->sec, offset);
                if (!insn) {
                        WARN("can't find insn for unwind_hints[%d]", i);
                        return -1;
@@@ -2522,6 -2535,9 +2535,9 @@@ static int classify_symbols(struct objt
        struct symbol *func;
  
        for_each_sym(file, func) {
+               if (func->type == STT_NOTYPE && strstarts(func->name, ".L"))
+                       func->local_label = true;
                if (func->bind != STB_GLOBAL)
                        continue;
  
@@@ -3620,18 -3636,6 +3636,18 @@@ static int validate_branch(struct objto
                                }
  
                                if (!save_insn->visited) {
 +                                      /*
 +                                       * If the restore hint insn is at the
 +                                       * beginning of a basic block and was
 +                                       * branched to from elsewhere, and the
 +                                       * save insn hasn't been visited yet,
 +                                       * defer following this branch for now.
 +                                       * It will be seen later via the
 +                                       * straight-line path.
 +                                       */
 +                                      if (!prev_insn)
 +                                              return 0;
 +
                                        WARN_INSN(insn, "objtool isn't smart enough to handle this CFI save/restore combo");
                                        return 1;
                                }
@@@ -3992,11 -3996,11 +4008,11 @@@ static int validate_retpoline(struct ob
  
                if (insn->type == INSN_RETURN) {
                        if (opts.rethunk) {
 -                              WARN_INSN(insn, "'naked' return found in RETHUNK build");
 +                              WARN_INSN(insn, "'naked' return found in MITIGATION_RETHUNK build");
                        } else
                                continue;
                } else {
 -                      WARN_INSN(insn, "indirect %s found in RETPOLINE build",
 +                      WARN_INSN(insn, "indirect %s found in MITIGATION_RETPOLINE build",
                                  insn->type == INSN_JUMP_DYNAMIC ? "jump" : "call");
                }