Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

objtool: Refactor jump table code

Now that C jump tables are supported, call them "jump tables" instead of
"switch tables". Also rename some other variables, add comments, and
simplify the code flow a bit.

Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Nick Desaulniers <ndesaulniers@google.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/cf951b0c0641628e0b9b81f7ceccd9bcabcb4bd8.1563413318.git.jpoimboe@redhat.com

authored by

Josh Poimboeuf and committed by
Thomas Gleixner
e7c2bc37 0c1ddd33

+46 -40
+44 -38
tools/objtool/check.c
··· 627 627 * However this code can't completely replace the 628 628 * read_symbols() code because this doesn't detect the 629 629 * case where the parent function's only reference to a 630 - * subfunction is through a switch table. 630 + * subfunction is through a jump table. 631 631 */ 632 632 if (!strstr(insn->func->name, ".cold.") && 633 633 strstr(insn->jump_dest->func->name, ".cold.")) { ··· 899 899 return ret; 900 900 } 901 901 902 - static int add_switch_table(struct objtool_file *file, struct instruction *insn, 902 + static int add_jump_table(struct objtool_file *file, struct instruction *insn, 903 903 struct rela *table, struct rela *next_table) 904 904 { 905 905 struct rela *rela = table; 906 - struct instruction *alt_insn; 906 + struct instruction *dest_insn; 907 907 struct alternative *alt; 908 908 struct symbol *pfunc = insn->func->pfunc; 909 909 unsigned int prev_offset = 0; 910 910 911 - list_for_each_entry_from(rela, &table->rela_sec->rela_list, list) { 911 + /* 912 + * Each @rela is a switch table relocation which points to the target 913 + * instruction. 914 + */ 915 + list_for_each_entry_from(rela, &table->sec->rela_list, list) { 912 916 if (rela == next_table) 913 917 break; 914 918 915 - /* Make sure the switch table entries are consecutive: */ 919 + /* Make sure the table entries are consecutive: */ 916 920 if (prev_offset && rela->offset != prev_offset + 8) 917 921 break; 918 922 ··· 925 921 rela->addend == pfunc->offset) 926 922 break; 927 923 928 - alt_insn = find_insn(file, rela->sym->sec, rela->addend); 929 - if (!alt_insn) 924 + dest_insn = find_insn(file, rela->sym->sec, rela->addend); 925 + if (!dest_insn) 930 926 break; 931 927 932 - /* Make sure the jmp dest is in the function or subfunction: */ 933 - if (alt_insn->func->pfunc != pfunc) 928 + /* Make sure the destination is in the same function: */ 929 + if (dest_insn->func->pfunc != pfunc) 934 930 break; 935 931 936 932 alt = malloc(sizeof(*alt)); ··· 939 935 return -1; 940 936 } 941 937 942 - alt->insn = alt_insn; 938 + alt->insn = dest_insn; 943 939 list_add_tail(&alt->list, &insn->alts); 944 940 prev_offset = rela->offset; 945 941 } ··· 954 950 } 955 951 956 952 /* 957 - * find_switch_table() - Given a dynamic jump, find the switch jump table in 953 + * find_jump_table() - Given a dynamic jump, find the switch jump table in 958 954 * .rodata associated with it. 959 955 * 960 956 * There are 3 basic patterns: ··· 996 992 * 997 993 * NOTE: RETPOLINE made it harder still to decode dynamic jumps. 998 994 */ 999 - static struct rela *find_switch_table(struct objtool_file *file, 995 + static struct rela *find_jump_table(struct objtool_file *file, 1000 996 struct symbol *func, 1001 997 struct instruction *insn) 1002 998 { 1003 - struct rela *text_rela, *rodata_rela; 999 + struct rela *text_rela, *table_rela; 1004 1000 struct instruction *orig_insn = insn; 1005 - struct section *rodata_sec; 1001 + struct section *table_sec; 1006 1002 unsigned long table_offset; 1007 1003 1008 1004 /* ··· 1035 1031 continue; 1036 1032 1037 1033 table_offset = text_rela->addend; 1038 - rodata_sec = text_rela->sym->sec; 1034 + table_sec = text_rela->sym->sec; 1039 1035 1040 1036 if (text_rela->type == R_X86_64_PC32) 1041 1037 table_offset += 4; ··· 1049 1045 * need to be placed in the C_JUMP_TABLE_SECTION section. They 1050 1046 * have symbols associated with them. 1051 1047 */ 1052 - if (find_symbol_containing(rodata_sec, table_offset) && 1053 - strcmp(rodata_sec->name, C_JUMP_TABLE_SECTION)) 1048 + if (find_symbol_containing(table_sec, table_offset) && 1049 + strcmp(table_sec->name, C_JUMP_TABLE_SECTION)) 1054 1050 continue; 1055 1051 1056 - rodata_rela = find_rela_by_dest(rodata_sec, table_offset); 1057 - if (rodata_rela) { 1058 - /* 1059 - * Use of RIP-relative switch jumps is quite rare, and 1060 - * indicates a rare GCC quirk/bug which can leave dead 1061 - * code behind. 1062 - */ 1063 - if (text_rela->type == R_X86_64_PC32) 1064 - file->ignore_unreachables = true; 1052 + /* Each table entry has a rela associated with it. */ 1053 + table_rela = find_rela_by_dest(table_sec, table_offset); 1054 + if (!table_rela) 1055 + continue; 1065 1056 1066 - return rodata_rela; 1067 - } 1057 + /* 1058 + * Use of RIP-relative switch jumps is quite rare, and 1059 + * indicates a rare GCC quirk/bug which can leave dead code 1060 + * behind. 1061 + */ 1062 + if (text_rela->type == R_X86_64_PC32) 1063 + file->ignore_unreachables = true; 1064 + 1065 + return table_rela; 1068 1066 } 1069 1067 1070 1068 return NULL; 1071 1069 } 1072 1070 1073 1071 1074 - static int add_func_switch_tables(struct objtool_file *file, 1072 + static int add_func_jump_tables(struct objtool_file *file, 1075 1073 struct symbol *func) 1076 1074 { 1077 1075 struct instruction *insn, *last = NULL, *prev_jump = NULL; ··· 1086 1080 1087 1081 /* 1088 1082 * Store back-pointers for unconditional forward jumps such 1089 - * that find_switch_table() can back-track using those and 1083 + * that find_jump_table() can back-track using those and 1090 1084 * avoid some potentially confusing code. 1091 1085 */ 1092 1086 if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest && ··· 1101 1095 if (insn->type != INSN_JUMP_DYNAMIC) 1102 1096 continue; 1103 1097 1104 - rela = find_switch_table(file, func, insn); 1098 + rela = find_jump_table(file, func, insn); 1105 1099 if (!rela) 1106 1100 continue; 1107 1101 1108 1102 /* 1109 - * We found a switch table, but we don't know yet how big it 1103 + * We found a jump table, but we don't know yet how big it 1110 1104 * is. Don't add it until we reach the end of the function or 1111 - * the beginning of another switch table in the same function. 1105 + * the beginning of another jump table in the same function. 1112 1106 */ 1113 1107 if (prev_jump) { 1114 - ret = add_switch_table(file, prev_jump, prev_rela, rela); 1108 + ret = add_jump_table(file, prev_jump, prev_rela, rela); 1115 1109 if (ret) 1116 1110 return ret; 1117 1111 } ··· 1121 1115 } 1122 1116 1123 1117 if (prev_jump) { 1124 - ret = add_switch_table(file, prev_jump, prev_rela, NULL); 1118 + ret = add_jump_table(file, prev_jump, prev_rela, NULL); 1125 1119 if (ret) 1126 1120 return ret; 1127 1121 } ··· 1134 1128 * section which contains a list of addresses within the function to jump to. 1135 1129 * This finds these jump tables and adds them to the insn->alts lists. 1136 1130 */ 1137 - static int add_switch_table_alts(struct objtool_file *file) 1131 + static int add_jump_table_alts(struct objtool_file *file) 1138 1132 { 1139 1133 struct section *sec; 1140 1134 struct symbol *func; ··· 1148 1142 if (func->type != STT_FUNC) 1149 1143 continue; 1150 1144 1151 - ret = add_func_switch_tables(file, func); 1145 + ret = add_func_jump_tables(file, func); 1152 1146 if (ret) 1153 1147 return ret; 1154 1148 } ··· 1345 1339 if (ret) 1346 1340 return ret; 1347 1341 1348 - ret = add_switch_table_alts(file); 1342 + ret = add_jump_table_alts(file); 1349 1343 if (ret) 1350 1344 return ret; 1351 1345
+1 -1
tools/objtool/elf.c
··· 385 385 rela->offset = rela->rela.r_offset; 386 386 symndx = GELF_R_SYM(rela->rela.r_info); 387 387 rela->sym = find_symbol_by_index(elf, symndx); 388 - rela->rela_sec = sec; 388 + rela->sec = sec; 389 389 if (!rela->sym) { 390 390 WARN("can't find rela entry symbol %d for %s", 391 391 symndx, sec->name);
+1 -1
tools/objtool/elf.h
··· 57 57 struct list_head list; 58 58 struct hlist_node hash; 59 59 GElf_Rela rela; 60 - struct section *rela_sec; 60 + struct section *sec; 61 61 struct symbol *sym; 62 62 unsigned int type; 63 63 unsigned long offset;