summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/compiler-gcc.h16
-rw-r--r--include/linux/compiler.h25
-rw-r--r--tools/objtool/arch.h5
-rw-r--r--tools/objtool/arch/x86/decode.c17
-rw-r--r--tools/objtool/check.c57
5 files changed, 94 insertions, 26 deletions
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 17cbe3cffc0e..dd4b4c59c2f0 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -128,22 +128,6 @@
#define __always_unused __attribute__((unused))
#define __mode(x) __attribute__((mode(x)))
-#ifdef CONFIG_STACK_VALIDATION
-#define annotate_unreachable() ({ \
- asm("%c0:\n\t" \
- ".pushsection .discard.unreachable\n\t" \
- ".long %c0b - .\n\t" \
- ".popsection\n\t" : : "i" (__LINE__)); \
-})
-#define ASM_UNREACHABLE \
- "999:\n\t" \
- ".pushsection .discard.unreachable\n\t" \
- ".long 999b - .\n\t" \
- ".popsection\n\t"
-#else
-#define annotate_unreachable()
-#endif
-
/* gcc version specific checks */
#if GCC_VERSION < 30200
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index 641f5912d75f..70f070ddd7c7 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -185,11 +185,34 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
#endif
/* Unreachable code */
+#ifdef CONFIG_STACK_VALIDATION
+#define annotate_reachable() ({ \
+ asm("%c0:\n\t" \
+ ".pushsection .discard.reachable\n\t" \
+ ".long %c0b - .\n\t" \
+ ".popsection\n\t" : : "i" (__LINE__)); \
+})
+#define annotate_unreachable() ({ \
+ asm("%c0:\n\t" \
+ ".pushsection .discard.unreachable\n\t" \
+ ".long %c0b - .\n\t" \
+ ".popsection\n\t" : : "i" (__LINE__)); \
+})
+#define ASM_UNREACHABLE \
+ "999:\n\t" \
+ ".pushsection .discard.unreachable\n\t" \
+ ".long 999b - .\n\t" \
+ ".popsection\n\t"
+#else
+#define annotate_reachable()
+#define annotate_unreachable()
+#endif
+
#ifndef ASM_UNREACHABLE
# define ASM_UNREACHABLE
#endif
#ifndef unreachable
-# define unreachable() do { } while (1)
+# define unreachable() do { annotate_reachable(); do { } while (1); } while (0)
#endif
/*
diff --git a/tools/objtool/arch.h b/tools/objtool/arch.h
index 21aeca874edb..b0d7dc3d71b5 100644
--- a/tools/objtool/arch.h
+++ b/tools/objtool/arch.h
@@ -31,8 +31,9 @@
#define INSN_RETURN 6
#define INSN_CONTEXT_SWITCH 7
#define INSN_STACK 8
-#define INSN_NOP 9
-#define INSN_OTHER 10
+#define INSN_BUG 9
+#define INSN_NOP 10
+#define INSN_OTHER 11
#define INSN_LAST INSN_OTHER
enum op_dest_type {
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index a36c2eba64e7..e4b400b67e41 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -382,20 +382,27 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
case 0x0f:
- if (op2 >= 0x80 && op2 <= 0x8f)
+ if (op2 >= 0x80 && op2 <= 0x8f) {
+
*type = INSN_JUMP_CONDITIONAL;
- else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 ||
- op2 == 0x35)
+
+ } else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 ||
+ op2 == 0x35) {
/* sysenter, sysret */
*type = INSN_CONTEXT_SWITCH;
- else if (op2 == 0x0d || op2 == 0x1f)
+ } else if (op2 == 0x0b || op2 == 0xb9) {
+
+ /* ud2 */
+ *type = INSN_BUG;
+
+ } else if (op2 == 0x0d || op2 == 0x1f) {
/* nopl/nopw */
*type = INSN_NOP;
- else if (op2 == 0xa0 || op2 == 0xa8) {
+ } else if (op2 == 0xa0 || op2 == 0xa8) {
/* push fs/gs */
*type = INSN_STACK;
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 3436a942b606..d07bf4a62b45 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -296,7 +296,7 @@ static int decode_instructions(struct objtool_file *file)
}
/*
- * Find all uses of the unreachable() macro, which are code path dead ends.
+ * Mark "ud2" instructions and manually annotated dead ends.
*/
static int add_dead_ends(struct objtool_file *file)
{
@@ -305,9 +305,20 @@ static int add_dead_ends(struct objtool_file *file)
struct instruction *insn;
bool found;
+ /*
+ * By default, "ud2" is a dead end unless otherwise annotated, because
+ * GCC 7 inserts it for certain divide-by-zero cases.
+ */
+ for_each_insn(file, insn)
+ if (insn->type == INSN_BUG)
+ insn->dead_end = true;
+
+ /*
+ * Check for manually annotated dead ends.
+ */
sec = find_section_by_name(file->elf, ".rela.discard.unreachable");
if (!sec)
- return 0;
+ goto reachable;
list_for_each_entry(rela, &sec->rela_list, list) {
if (rela->sym->type != STT_SECTION) {
@@ -340,6 +351,48 @@ static int add_dead_ends(struct objtool_file *file)
insn->dead_end = true;
}
+reachable:
+ /*
+ * These manually annotated reachable checks are needed for GCC 4.4,
+ * where the Linux unreachable() macro isn't supported. In that case
+ * GCC doesn't know the "ud2" is fatal, so it generates code as if it's
+ * not a dead end.
+ */
+ sec = find_section_by_name(file->elf, ".rela.discard.reachable");
+ 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 %s", sec->name);
+ 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 reachable insn at %s+0x%x",
+ rela->sym->sec->name, rela->addend);
+ return -1;
+ }
+ } else {
+ WARN("can't find reachable insn at %s+0x%x",
+ rela->sym->sec->name, rela->addend);
+ return -1;
+ }
+
+ insn->dead_end = false;
+ }
+
return 0;
}