diff options
Diffstat (limited to 'tools/objtool/check.c')
-rw-r--r-- | tools/objtool/check.c | 53 |
1 files changed, 48 insertions, 5 deletions
diff --git a/tools/objtool/check.c b/tools/objtool/check.c index b00b1896547e..a8cb69a26576 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c | |||
@@ -852,8 +852,14 @@ static int add_switch_table(struct objtool_file *file, struct symbol *func, | |||
852 | * This is a fairly uncommon pattern which is new for GCC 6. As of this | 852 | * This is a fairly uncommon pattern which is new for GCC 6. As of this |
853 | * writing, there are 11 occurrences of it in the allmodconfig kernel. | 853 | * writing, there are 11 occurrences of it in the allmodconfig kernel. |
854 | * | 854 | * |
855 | * As of GCC 7 there are quite a few more of these and the 'in between' code | ||
856 | * is significant. Esp. with KASAN enabled some of the code between the mov | ||
857 | * and jmpq uses .rodata itself, which can confuse things. | ||
858 | * | ||
855 | * TODO: Once we have DWARF CFI and smarter instruction decoding logic, | 859 | * TODO: Once we have DWARF CFI and smarter instruction decoding logic, |
856 | * ensure the same register is used in the mov and jump instructions. | 860 | * ensure the same register is used in the mov and jump instructions. |
861 | * | ||
862 | * NOTE: RETPOLINE made it harder still to decode dynamic jumps. | ||
857 | */ | 863 | */ |
858 | static struct rela *find_switch_table(struct objtool_file *file, | 864 | static struct rela *find_switch_table(struct objtool_file *file, |
859 | struct symbol *func, | 865 | struct symbol *func, |
@@ -875,12 +881,25 @@ static struct rela *find_switch_table(struct objtool_file *file, | |||
875 | text_rela->addend + 4); | 881 | text_rela->addend + 4); |
876 | if (!rodata_rela) | 882 | if (!rodata_rela) |
877 | return NULL; | 883 | return NULL; |
884 | |||
878 | file->ignore_unreachables = true; | 885 | file->ignore_unreachables = true; |
879 | return rodata_rela; | 886 | return rodata_rela; |
880 | } | 887 | } |
881 | 888 | ||
882 | /* case 3 */ | 889 | /* case 3 */ |
883 | func_for_each_insn_continue_reverse(file, func, insn) { | 890 | /* |
891 | * Backward search using the @first_jump_src links, these help avoid | ||
892 | * much of the 'in between' code. Which avoids us getting confused by | ||
893 | * it. | ||
894 | */ | ||
895 | for (insn = list_prev_entry(insn, list); | ||
896 | |||
897 | &insn->list != &file->insn_list && | ||
898 | insn->sec == func->sec && | ||
899 | insn->offset >= func->offset; | ||
900 | |||
901 | insn = insn->first_jump_src ?: list_prev_entry(insn, list)) { | ||
902 | |||
884 | if (insn->type == INSN_JUMP_DYNAMIC) | 903 | if (insn->type == INSN_JUMP_DYNAMIC) |
885 | break; | 904 | break; |
886 | 905 | ||
@@ -910,14 +929,32 @@ static struct rela *find_switch_table(struct objtool_file *file, | |||
910 | return NULL; | 929 | return NULL; |
911 | } | 930 | } |
912 | 931 | ||
932 | |||
913 | static int add_func_switch_tables(struct objtool_file *file, | 933 | static int add_func_switch_tables(struct objtool_file *file, |
914 | struct symbol *func) | 934 | struct symbol *func) |
915 | { | 935 | { |
916 | struct instruction *insn, *prev_jump = NULL; | 936 | struct instruction *insn, *last = NULL, *prev_jump = NULL; |
917 | struct rela *rela, *prev_rela = NULL; | 937 | struct rela *rela, *prev_rela = NULL; |
918 | int ret; | 938 | int ret; |
919 | 939 | ||
920 | func_for_each_insn(file, func, insn) { | 940 | func_for_each_insn(file, func, insn) { |
941 | if (!last) | ||
942 | last = insn; | ||
943 | |||
944 | /* | ||
945 | * Store back-pointers for unconditional forward jumps such | ||
946 | * that find_switch_table() can back-track using those and | ||
947 | * avoid some potentially confusing code. | ||
948 | */ | ||
949 | if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest && | ||
950 | insn->offset > last->offset && | ||
951 | insn->jump_dest->offset > insn->offset && | ||
952 | !insn->jump_dest->first_jump_src) { | ||
953 | |||
954 | insn->jump_dest->first_jump_src = insn; | ||
955 | last = insn->jump_dest; | ||
956 | } | ||
957 | |||
921 | if (insn->type != INSN_JUMP_DYNAMIC) | 958 | if (insn->type != INSN_JUMP_DYNAMIC) |
922 | continue; | 959 | continue; |
923 | 960 | ||
@@ -1899,13 +1936,19 @@ static bool ignore_unreachable_insn(struct instruction *insn) | |||
1899 | if (is_kasan_insn(insn) || is_ubsan_insn(insn)) | 1936 | if (is_kasan_insn(insn) || is_ubsan_insn(insn)) |
1900 | return true; | 1937 | return true; |
1901 | 1938 | ||
1902 | if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest) { | 1939 | if (insn->type == INSN_JUMP_UNCONDITIONAL) { |
1903 | insn = insn->jump_dest; | 1940 | if (insn->jump_dest && |
1904 | continue; | 1941 | insn->jump_dest->func == insn->func) { |
1942 | insn = insn->jump_dest; | ||
1943 | continue; | ||
1944 | } | ||
1945 | |||
1946 | break; | ||
1905 | } | 1947 | } |
1906 | 1948 | ||
1907 | if (insn->offset + insn->len >= insn->func->offset + insn->func->len) | 1949 | if (insn->offset + insn->len >= insn->func->offset + insn->func->len) |
1908 | break; | 1950 | break; |
1951 | |||
1909 | insn = list_next_entry(insn, list); | 1952 | insn = list_next_entry(insn, list); |
1910 | } | 1953 | } |
1911 | 1954 | ||