#endif
 #endif
 
+#ifdef CONFIG_STACK_VALIDATION
+#define annotate_unreachable() ({                                      \
+       asm("1:\t\n"                                                    \
+           ".pushsection __unreachable, \"a\"\t\n"                     \
+           ".long 1b\t\n"                                              \
+           ".popsection\t\n");                                         \
+})
+#else
+#define annotate_unreachable()
+#endif
+
 /*
  * Mark a position in code as unreachable.  This can be used to
  * suppress control flow warnings after asm blocks that transfer
  * this in the preprocessor, but we can live with this because they're
  * unreleased.  Really, we need to have autoconf for the kernel.
  */
-#define unreachable() __builtin_unreachable()
+#define unreachable() annotate_unreachable(); __builtin_unreachable()
 
 /* Mark a function definition as prohibited from being cloned. */
 #define __noclone      __attribute__((__noclone__, __optimize__("no-tracer")))
 
        unsigned int len, state;
        unsigned char type;
        unsigned long immediate;
-       bool alt_group, visited;
+       bool alt_group, visited, dead_end;
        struct symbol *call_dest;
        struct instruction *jump_dest;
        struct list_head alts;
        return 0;
 }
 
+/*
+ * Find all uses of the unreachable() macro, which are code path dead ends.
+ */
+static int add_dead_ends(struct objtool_file *file)
+{
+       struct section *sec;
+       struct rela *rela;
+       struct instruction *insn;
+       bool found;
+
+       sec = find_section_by_name(file->elf, ".rela__unreachable");
+       if (!sec)
+               return 0;
+
+       list_for_each_entry(rela, &sec->rela_list, list) {
+               if (rela->sym->type != STT_SECTION) {
+                       WARN("unexpected relocation symbol type in .rela__unreachable");
+                       return -1;
+               }
+               insn = find_insn(file, rela->sym->sec, rela->addend);
+               if (insn)
+                       insn = list_prev_entry(insn, list);
+               else if (rela->addend == rela->sym->sec->len) {
+                       found = false;
+                       list_for_each_entry_reverse(insn, &file->insn_list, list) {
+                               if (insn->sec == rela->sym->sec) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+
+                       if (!found) {
+                               WARN("can't find unreachable insn at %s+0x%x",
+                                    rela->sym->sec->name, rela->addend);
+                               return -1;
+                       }
+               } else {
+                       WARN("can't find unreachable insn at %s+0x%x",
+                            rela->sym->sec->name, rela->addend);
+                       return -1;
+               }
+
+               insn->dead_end = true;
+       }
+
+       return 0;
+}
+
 /*
  * Warnings shouldn't be reported for ignored functions.
  */
        if (ret)
                return ret;
 
+       ret = add_dead_ends(file);
+       if (ret)
+               return ret;
+
        add_ignores(file);
 
        ret = add_jump_destinations(file);
 
                        return 0;
 
-               case INSN_BUG:
-                       return 0;
-
                default:
                        break;
                }
 
+               if (insn->dead_end)
+                       return 0;
+
                insn = next_insn_same_sec(file, insn);
                if (!insn) {
                        WARN("%s: unexpected end of section", sec->name);