1 // SPDX-License-Identifier: GPL-2.0-or-later 2 #include <string.h> 3 #include <objtool/special.h> 4 #include <objtool/warn.h> 5 6 bool arch_support_alt_relocation(struct special_alt *special_alt, 7 struct instruction *insn, 8 struct reloc *reloc) 9 { 10 return false; 11 } 12 13 struct table_info { 14 struct list_head jump_info; 15 unsigned long insn_offset; 16 unsigned long rodata_offset; 17 }; 18 19 static void get_rodata_table_size_by_table_annotate(struct objtool_file *file, 20 struct instruction *insn, 21 unsigned long *table_size) 22 { 23 struct section *rsec; 24 struct reloc *reloc; 25 struct list_head table_list; 26 struct table_info *orig_table; 27 struct table_info *next_table; 28 unsigned long tmp_insn_offset; 29 unsigned long tmp_rodata_offset; 30 bool is_valid_list = false; 31 32 rsec = find_section_by_name(file->elf, ".rela.discard.tablejump_annotate"); 33 if (!rsec) 34 return; 35 36 INIT_LIST_HEAD(&table_list); 37 38 for_each_reloc(rsec, reloc) { 39 if (reloc->sym->sec->rodata) 40 continue; 41 42 if (strcmp(insn->sec->name, reloc->sym->sec->name)) 43 continue; 44 45 orig_table = malloc(sizeof(struct table_info)); 46 if (!orig_table) { 47 WARN("malloc failed"); 48 return; 49 } 50 51 orig_table->insn_offset = reloc->sym->offset + reloc_addend(reloc); 52 reloc++; 53 orig_table->rodata_offset = reloc->sym->offset + reloc_addend(reloc); 54 55 list_add_tail(&orig_table->jump_info, &table_list); 56 57 if (reloc_idx(reloc) + 1 == sec_num_entries(rsec)) 58 break; 59 60 if (strcmp(insn->sec->name, (reloc + 1)->sym->sec->name)) { 61 list_for_each_entry(orig_table, &table_list, jump_info) { 62 if (orig_table->insn_offset == insn->offset) { 63 is_valid_list = true; 64 break; 65 } 66 } 67 68 if (!is_valid_list) { 69 list_del_init(&table_list); 70 continue; 71 } 72 73 break; 74 } 75 } 76 77 list_for_each_entry(orig_table, &table_list, jump_info) { 78 next_table = list_next_entry(orig_table, jump_info); 79 list_for_each_entry_from(next_table, &table_list, jump_info) { 80 if (next_table->rodata_offset < orig_table->rodata_offset) { 81 tmp_insn_offset = next_table->insn_offset; 82 tmp_rodata_offset = next_table->rodata_offset; 83 next_table->insn_offset = orig_table->insn_offset; 84 next_table->rodata_offset = orig_table->rodata_offset; 85 orig_table->insn_offset = tmp_insn_offset; 86 orig_table->rodata_offset = tmp_rodata_offset; 87 } 88 } 89 } 90 91 list_for_each_entry(orig_table, &table_list, jump_info) { 92 if (insn->offset == orig_table->insn_offset) { 93 next_table = list_next_entry(orig_table, jump_info); 94 if (&next_table->jump_info == &table_list) { 95 *table_size = 0; 96 return; 97 } 98 99 while (next_table->rodata_offset == orig_table->rodata_offset) { 100 next_table = list_next_entry(next_table, jump_info); 101 if (&next_table->jump_info == &table_list) { 102 *table_size = 0; 103 return; 104 } 105 } 106 107 *table_size = next_table->rodata_offset - orig_table->rodata_offset; 108 } 109 } 110 } 111 112 static struct reloc *find_reloc_by_table_annotate(struct objtool_file *file, 113 struct instruction *insn, 114 unsigned long *table_size) 115 { 116 struct section *rsec; 117 struct reloc *reloc; 118 unsigned long offset; 119 120 rsec = find_section_by_name(file->elf, ".rela.discard.tablejump_annotate"); 121 if (!rsec) 122 return NULL; 123 124 for_each_reloc(rsec, reloc) { 125 if (reloc->sym->sec->rodata) 126 continue; 127 128 if (strcmp(insn->sec->name, reloc->sym->sec->name)) 129 continue; 130 131 offset = reloc->sym->offset + reloc_addend(reloc); 132 if (insn->offset == offset) { 133 get_rodata_table_size_by_table_annotate(file, insn, table_size); 134 reloc++; 135 return reloc; 136 } 137 } 138 139 return NULL; 140 } 141 142 static struct reloc *find_reloc_of_rodata_c_jump_table(struct section *sec, 143 unsigned long offset, 144 unsigned long *table_size) 145 { 146 struct section *rsec; 147 struct reloc *reloc; 148 149 rsec = sec->rsec; 150 if (!rsec) 151 return NULL; 152 153 for_each_reloc(rsec, reloc) { 154 if (reloc_offset(reloc) > offset) 155 break; 156 157 if (!strcmp(reloc->sym->sec->name, C_JUMP_TABLE_SECTION)) { 158 *table_size = 0; 159 return reloc; 160 } 161 } 162 163 return NULL; 164 } 165 166 struct reloc *arch_find_switch_table(struct objtool_file *file, 167 struct instruction *insn, 168 unsigned long *table_size) 169 { 170 struct reloc *annotate_reloc; 171 struct reloc *rodata_reloc; 172 struct section *table_sec; 173 unsigned long table_offset; 174 175 annotate_reloc = find_reloc_by_table_annotate(file, insn, table_size); 176 if (!annotate_reloc) { 177 annotate_reloc = find_reloc_of_rodata_c_jump_table( 178 insn->sec, insn->offset, table_size); 179 if (!annotate_reloc) 180 return NULL; 181 } 182 183 table_sec = annotate_reloc->sym->sec; 184 table_offset = annotate_reloc->sym->offset + reloc_addend(annotate_reloc); 185 186 /* 187 * Each table entry has a rela associated with it. The rela 188 * should reference text in the same function as the original 189 * instruction. 190 */ 191 rodata_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset); 192 if (!rodata_reloc) 193 return NULL; 194 195 return rodata_reloc; 196 } 197