diff options
-rw-r--r-- | scripts/Makefile.build | 4 | ||||
-rw-r--r-- | tools/objtool/builtin-check.c | 3 | ||||
-rw-r--r-- | tools/objtool/builtin.h | 2 | ||||
-rw-r--r-- | tools/objtool/check.c | 86 | ||||
-rw-r--r-- | tools/objtool/check.h | 1 |
5 files changed, 93 insertions, 3 deletions
diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 47cddf32aeba..53d862aee335 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build | |||
@@ -264,6 +264,10 @@ objtool_args += --no-unreachable | |||
264 | else | 264 | else |
265 | objtool_args += $(call cc-ifversion, -lt, 0405, --no-unreachable) | 265 | objtool_args += $(call cc-ifversion, -lt, 0405, --no-unreachable) |
266 | endif | 266 | endif |
267 | ifdef CONFIG_RETPOLINE | ||
268 | objtool_args += --retpoline | ||
269 | endif | ||
270 | |||
267 | 271 | ||
268 | ifdef CONFIG_MODVERSIONS | 272 | ifdef CONFIG_MODVERSIONS |
269 | objtool_o = $(@D)/.tmp_$(@F) | 273 | objtool_o = $(@D)/.tmp_$(@F) |
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 8d0986d2a803..dd6bcd6097f5 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c | |||
@@ -29,7 +29,7 @@ | |||
29 | #include "builtin.h" | 29 | #include "builtin.h" |
30 | #include "check.h" | 30 | #include "check.h" |
31 | 31 | ||
32 | bool no_fp, no_unreachable; | 32 | bool no_fp, no_unreachable, retpoline; |
33 | 33 | ||
34 | static const char * const check_usage[] = { | 34 | static const char * const check_usage[] = { |
35 | "objtool check [<options>] file.o", | 35 | "objtool check [<options>] file.o", |
@@ -39,6 +39,7 @@ static const char * const check_usage[] = { | |||
39 | const struct option check_options[] = { | 39 | const struct option check_options[] = { |
40 | OPT_BOOLEAN('f', "no-fp", &no_fp, "Skip frame pointer validation"), | 40 | OPT_BOOLEAN('f', "no-fp", &no_fp, "Skip frame pointer validation"), |
41 | OPT_BOOLEAN('u', "no-unreachable", &no_unreachable, "Skip 'unreachable instruction' warnings"), | 41 | OPT_BOOLEAN('u', "no-unreachable", &no_unreachable, "Skip 'unreachable instruction' warnings"), |
42 | OPT_BOOLEAN('r', "retpoline", &retpoline, "Validate retpoline assumptions"), | ||
42 | OPT_END(), | 43 | OPT_END(), |
43 | }; | 44 | }; |
44 | 45 | ||
diff --git a/tools/objtool/builtin.h b/tools/objtool/builtin.h index f166ea1b1da2..7b6addfce045 100644 --- a/tools/objtool/builtin.h +++ b/tools/objtool/builtin.h | |||
@@ -20,7 +20,7 @@ | |||
20 | #include <subcmd/parse-options.h> | 20 | #include <subcmd/parse-options.h> |
21 | 21 | ||
22 | extern const struct option check_options[]; | 22 | extern const struct option check_options[]; |
23 | extern bool no_fp, no_unreachable; | 23 | extern bool no_fp, no_unreachable, retpoline; |
24 | 24 | ||
25 | extern int cmd_check(int argc, const char **argv); | 25 | extern int cmd_check(int argc, const char **argv); |
26 | extern int cmd_orc(int argc, const char **argv); | 26 | extern int cmd_orc(int argc, const char **argv); |
diff --git a/tools/objtool/check.c b/tools/objtool/check.c index ab6f0de7f90d..5e5db7b4d77b 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c | |||
@@ -497,6 +497,7 @@ static int add_jump_destinations(struct objtool_file *file) | |||
497 | * disguise, so convert them accordingly. | 497 | * disguise, so convert them accordingly. |
498 | */ | 498 | */ |
499 | insn->type = INSN_JUMP_DYNAMIC; | 499 | insn->type = INSN_JUMP_DYNAMIC; |
500 | insn->retpoline_safe = true; | ||
500 | continue; | 501 | continue; |
501 | } else { | 502 | } else { |
502 | /* sibling call */ | 503 | /* sibling call */ |
@@ -548,7 +549,8 @@ static int add_call_destinations(struct objtool_file *file) | |||
548 | if (!insn->call_dest && !insn->ignore) { | 549 | if (!insn->call_dest && !insn->ignore) { |
549 | WARN_FUNC("unsupported intra-function call", | 550 | WARN_FUNC("unsupported intra-function call", |
550 | insn->sec, insn->offset); | 551 | insn->sec, insn->offset); |
551 | WARN("If this is a retpoline, please patch it in with alternatives and annotate it with ANNOTATE_NOSPEC_ALTERNATIVE."); | 552 | if (retpoline) |
553 | WARN("If this is a retpoline, please patch it in with alternatives and annotate it with ANNOTATE_NOSPEC_ALTERNATIVE."); | ||
552 | return -1; | 554 | return -1; |
553 | } | 555 | } |
554 | 556 | ||
@@ -1108,6 +1110,54 @@ static int read_unwind_hints(struct objtool_file *file) | |||
1108 | return 0; | 1110 | return 0; |
1109 | } | 1111 | } |
1110 | 1112 | ||
1113 | static int read_retpoline_hints(struct objtool_file *file) | ||
1114 | { | ||
1115 | struct section *sec, *relasec; | ||
1116 | struct instruction *insn; | ||
1117 | struct rela *rela; | ||
1118 | int i; | ||
1119 | |||
1120 | sec = find_section_by_name(file->elf, ".discard.retpoline_safe"); | ||
1121 | if (!sec) | ||
1122 | return 0; | ||
1123 | |||
1124 | relasec = sec->rela; | ||
1125 | if (!relasec) { | ||
1126 | WARN("missing .rela.discard.retpoline_safe section"); | ||
1127 | return -1; | ||
1128 | } | ||
1129 | |||
1130 | if (sec->len % sizeof(unsigned long)) { | ||
1131 | WARN("retpoline_safe size mismatch: %d %ld", sec->len, sizeof(unsigned long)); | ||
1132 | return -1; | ||
1133 | } | ||
1134 | |||
1135 | for (i = 0; i < sec->len / sizeof(unsigned long); i++) { | ||
1136 | rela = find_rela_by_dest(sec, i * sizeof(unsigned long)); | ||
1137 | if (!rela) { | ||
1138 | WARN("can't find rela for retpoline_safe[%d]", i); | ||
1139 | return -1; | ||
1140 | } | ||
1141 | |||
1142 | insn = find_insn(file, rela->sym->sec, rela->addend); | ||
1143 | if (!insn) { | ||
1144 | WARN("can't find insn for retpoline_safe[%d]", i); | ||
1145 | return -1; | ||
1146 | } | ||
1147 | |||
1148 | if (insn->type != INSN_JUMP_DYNAMIC && | ||
1149 | insn->type != INSN_CALL_DYNAMIC) { | ||
1150 | WARN_FUNC("retpoline_safe hint not a indirect jump/call", | ||
1151 | insn->sec, insn->offset); | ||
1152 | return -1; | ||
1153 | } | ||
1154 | |||
1155 | insn->retpoline_safe = true; | ||
1156 | } | ||
1157 | |||
1158 | return 0; | ||
1159 | } | ||
1160 | |||
1111 | static int decode_sections(struct objtool_file *file) | 1161 | static int decode_sections(struct objtool_file *file) |
1112 | { | 1162 | { |
1113 | int ret; | 1163 | int ret; |
@@ -1146,6 +1196,10 @@ static int decode_sections(struct objtool_file *file) | |||
1146 | if (ret) | 1196 | if (ret) |
1147 | return ret; | 1197 | return ret; |
1148 | 1198 | ||
1199 | ret = read_retpoline_hints(file); | ||
1200 | if (ret) | ||
1201 | return ret; | ||
1202 | |||
1149 | return 0; | 1203 | return 0; |
1150 | } | 1204 | } |
1151 | 1205 | ||
@@ -1891,6 +1945,29 @@ static int validate_unwind_hints(struct objtool_file *file) | |||
1891 | return warnings; | 1945 | return warnings; |
1892 | } | 1946 | } |
1893 | 1947 | ||
1948 | static int validate_retpoline(struct objtool_file *file) | ||
1949 | { | ||
1950 | struct instruction *insn; | ||
1951 | int warnings = 0; | ||
1952 | |||
1953 | for_each_insn(file, insn) { | ||
1954 | if (insn->type != INSN_JUMP_DYNAMIC && | ||
1955 | insn->type != INSN_CALL_DYNAMIC) | ||
1956 | continue; | ||
1957 | |||
1958 | if (insn->retpoline_safe) | ||
1959 | continue; | ||
1960 | |||
1961 | WARN_FUNC("indirect %s found in RETPOLINE build", | ||
1962 | insn->sec, insn->offset, | ||
1963 | insn->type == INSN_JUMP_DYNAMIC ? "jump" : "call"); | ||
1964 | |||
1965 | warnings++; | ||
1966 | } | ||
1967 | |||
1968 | return warnings; | ||
1969 | } | ||
1970 | |||
1894 | static bool is_kasan_insn(struct instruction *insn) | 1971 | static bool is_kasan_insn(struct instruction *insn) |
1895 | { | 1972 | { |
1896 | return (insn->type == INSN_CALL && | 1973 | return (insn->type == INSN_CALL && |
@@ -2051,6 +2128,13 @@ int check(const char *_objname, bool orc) | |||
2051 | if (list_empty(&file.insn_list)) | 2128 | if (list_empty(&file.insn_list)) |
2052 | goto out; | 2129 | goto out; |
2053 | 2130 | ||
2131 | if (retpoline) { | ||
2132 | ret = validate_retpoline(&file); | ||
2133 | if (ret < 0) | ||
2134 | return ret; | ||
2135 | warnings += ret; | ||
2136 | } | ||
2137 | |||
2054 | ret = validate_functions(&file); | 2138 | ret = validate_functions(&file); |
2055 | if (ret < 0) | 2139 | if (ret < 0) |
2056 | goto out; | 2140 | goto out; |
diff --git a/tools/objtool/check.h b/tools/objtool/check.h index 936255ba23db..c6b68fcb926f 100644 --- a/tools/objtool/check.h +++ b/tools/objtool/check.h | |||
@@ -45,6 +45,7 @@ struct instruction { | |||
45 | unsigned char type; | 45 | unsigned char type; |
46 | unsigned long immediate; | 46 | unsigned long immediate; |
47 | bool alt_group, visited, dead_end, ignore, hint, save, restore, ignore_alts; | 47 | bool alt_group, visited, dead_end, ignore, hint, save, restore, ignore_alts; |
48 | bool retpoline_safe; | ||
48 | struct symbol *call_dest; | 49 | struct symbol *call_dest; |
49 | struct instruction *jump_dest; | 50 | struct instruction *jump_dest; |
50 | struct instruction *first_jump_src; | 51 | struct instruction *first_jump_src; |