diff options
Diffstat (limited to 'tools/objtool/check.c')
-rw-r--r-- | tools/objtool/check.c | 93 |
1 files changed, 88 insertions, 5 deletions
diff --git a/tools/objtool/check.c b/tools/objtool/check.c index a8cb69a26576..92b6a2c21631 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <string.h> | 18 | #include <string.h> |
19 | #include <stdlib.h> | 19 | #include <stdlib.h> |
20 | 20 | ||
21 | #include "builtin.h" | ||
21 | #include "check.h" | 22 | #include "check.h" |
22 | #include "elf.h" | 23 | #include "elf.h" |
23 | #include "special.h" | 24 | #include "special.h" |
@@ -33,7 +34,6 @@ struct alternative { | |||
33 | }; | 34 | }; |
34 | 35 | ||
35 | const char *objname; | 36 | const char *objname; |
36 | static bool no_fp; | ||
37 | struct cfi_state initial_func_cfi; | 37 | struct cfi_state initial_func_cfi; |
38 | 38 | ||
39 | struct instruction *find_insn(struct objtool_file *file, | 39 | struct instruction *find_insn(struct objtool_file *file, |
@@ -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 | ||
@@ -923,7 +925,11 @@ static struct rela *find_switch_table(struct objtool_file *file, | |||
923 | if (find_symbol_containing(file->rodata, text_rela->addend)) | 925 | if (find_symbol_containing(file->rodata, text_rela->addend)) |
924 | continue; | 926 | continue; |
925 | 927 | ||
926 | return find_rela_by_dest(file->rodata, text_rela->addend); | 928 | rodata_rela = find_rela_by_dest(file->rodata, text_rela->addend); |
929 | if (!rodata_rela) | ||
930 | continue; | ||
931 | |||
932 | return rodata_rela; | ||
927 | } | 933 | } |
928 | 934 | ||
929 | return NULL; | 935 | return NULL; |
@@ -1108,6 +1114,41 @@ static int read_unwind_hints(struct objtool_file *file) | |||
1108 | return 0; | 1114 | return 0; |
1109 | } | 1115 | } |
1110 | 1116 | ||
1117 | static int read_retpoline_hints(struct objtool_file *file) | ||
1118 | { | ||
1119 | struct section *sec; | ||
1120 | struct instruction *insn; | ||
1121 | struct rela *rela; | ||
1122 | |||
1123 | sec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe"); | ||
1124 | if (!sec) | ||
1125 | return 0; | ||
1126 | |||
1127 | list_for_each_entry(rela, &sec->rela_list, list) { | ||
1128 | if (rela->sym->type != STT_SECTION) { | ||
1129 | WARN("unexpected relocation symbol type in %s", sec->name); | ||
1130 | return -1; | ||
1131 | } | ||
1132 | |||
1133 | insn = find_insn(file, rela->sym->sec, rela->addend); | ||
1134 | if (!insn) { | ||
1135 | WARN("bad .discard.retpoline_safe entry"); | ||
1136 | return -1; | ||
1137 | } | ||
1138 | |||
1139 | if (insn->type != INSN_JUMP_DYNAMIC && | ||
1140 | insn->type != INSN_CALL_DYNAMIC) { | ||
1141 | WARN_FUNC("retpoline_safe hint not an indirect jump/call", | ||
1142 | insn->sec, insn->offset); | ||
1143 | return -1; | ||
1144 | } | ||
1145 | |||
1146 | insn->retpoline_safe = true; | ||
1147 | } | ||
1148 | |||
1149 | return 0; | ||
1150 | } | ||
1151 | |||
1111 | static int decode_sections(struct objtool_file *file) | 1152 | static int decode_sections(struct objtool_file *file) |
1112 | { | 1153 | { |
1113 | int ret; | 1154 | int ret; |
@@ -1146,6 +1187,10 @@ static int decode_sections(struct objtool_file *file) | |||
1146 | if (ret) | 1187 | if (ret) |
1147 | return ret; | 1188 | return ret; |
1148 | 1189 | ||
1190 | ret = read_retpoline_hints(file); | ||
1191 | if (ret) | ||
1192 | return ret; | ||
1193 | |||
1149 | return 0; | 1194 | return 0; |
1150 | } | 1195 | } |
1151 | 1196 | ||
@@ -1891,6 +1936,38 @@ static int validate_unwind_hints(struct objtool_file *file) | |||
1891 | return warnings; | 1936 | return warnings; |
1892 | } | 1937 | } |
1893 | 1938 | ||
1939 | static int validate_retpoline(struct objtool_file *file) | ||
1940 | { | ||
1941 | struct instruction *insn; | ||
1942 | int warnings = 0; | ||
1943 | |||
1944 | for_each_insn(file, insn) { | ||
1945 | if (insn->type != INSN_JUMP_DYNAMIC && | ||
1946 | insn->type != INSN_CALL_DYNAMIC) | ||
1947 | continue; | ||
1948 | |||
1949 | if (insn->retpoline_safe) | ||
1950 | continue; | ||
1951 | |||
1952 | /* | ||
1953 | * .init.text code is ran before userspace and thus doesn't | ||
1954 | * strictly need retpolines, except for modules which are | ||
1955 | * loaded late, they very much do need retpoline in their | ||
1956 | * .init.text | ||
1957 | */ | ||
1958 | if (!strcmp(insn->sec->name, ".init.text") && !module) | ||
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 && |
@@ -2022,13 +2099,12 @@ static void cleanup(struct objtool_file *file) | |||
2022 | elf_close(file->elf); | 2099 | elf_close(file->elf); |
2023 | } | 2100 | } |
2024 | 2101 | ||
2025 | int check(const char *_objname, bool _no_fp, bool no_unreachable, bool orc) | 2102 | int check(const char *_objname, bool orc) |
2026 | { | 2103 | { |
2027 | struct objtool_file file; | 2104 | struct objtool_file file; |
2028 | int ret, warnings = 0; | 2105 | int ret, warnings = 0; |
2029 | 2106 | ||
2030 | objname = _objname; | 2107 | objname = _objname; |
2031 | no_fp = _no_fp; | ||
2032 | 2108 | ||
2033 | file.elf = elf_open(objname, orc ? O_RDWR : O_RDONLY); | 2109 | file.elf = elf_open(objname, orc ? O_RDWR : O_RDONLY); |
2034 | if (!file.elf) | 2110 | if (!file.elf) |
@@ -2052,6 +2128,13 @@ int check(const char *_objname, bool _no_fp, bool no_unreachable, bool orc) | |||
2052 | if (list_empty(&file.insn_list)) | 2128 | if (list_empty(&file.insn_list)) |
2053 | goto out; | 2129 | goto out; |
2054 | 2130 | ||
2131 | if (retpoline) { | ||
2132 | ret = validate_retpoline(&file); | ||
2133 | if (ret < 0) | ||
2134 | return ret; | ||
2135 | warnings += ret; | ||
2136 | } | ||
2137 | |||
2055 | ret = validate_functions(&file); | 2138 | ret = validate_functions(&file); |
2056 | if (ret < 0) | 2139 | if (ret < 0) |
2057 | goto out; | 2140 | goto out; |