diff options
Diffstat (limited to 'tools/objtool/check.c')
-rw-r--r-- | tools/objtool/check.c | 86 |
1 files changed, 85 insertions, 1 deletions
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; |