diff options
Diffstat (limited to 'tools')
| -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 |
4 files changed, 89 insertions, 3 deletions
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; |
