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 31 rsec = find_section_by_name(file->elf, ".rela.discard.tablejump_annotate"); 32 if (!rsec) 33 return; 34 35 INIT_LIST_HEAD(&table_list); 36 37 for_each_reloc(rsec, reloc) { 38 orig_table = malloc(sizeof(struct table_info)); 39 if (!orig_table) { 40 WARN("malloc failed"); 41 return; 42 } 43 44 orig_table->insn_offset = reloc->sym->offset + reloc_addend(reloc); 45 reloc++; 46 orig_table->rodata_offset = reloc->sym->offset + reloc_addend(reloc); 47 48 list_add_tail(&orig_table->jump_info, &table_list); 49 50 if (reloc_idx(reloc) + 1 == sec_num_entries(rsec)) 51 break; 52 } 53 54 list_for_each_entry(orig_table, &table_list, jump_info) { 55 next_table = list_next_entry(orig_table, jump_info); 56 list_for_each_entry_from(next_table, &table_list, jump_info) { 57 if (next_table->rodata_offset < orig_table->rodata_offset) { 58 tmp_insn_offset = next_table->insn_offset; 59 tmp_rodata_offset = next_table->rodata_offset; 60 next_table->insn_offset = orig_table->insn_offset; 61 next_table->rodata_offset = orig_table->rodata_offset; 62 orig_table->insn_offset = tmp_insn_offset; 63 orig_table->rodata_offset = tmp_rodata_offset; 64 } 65 } 66 } 67 68 list_for_each_entry(orig_table, &table_list, jump_info) { 69 if (insn->offset == orig_table->insn_offset) { 70 next_table = list_next_entry(orig_table, jump_info); 71 if (&next_table->jump_info == &table_list) { 72 *table_size = 0; 73 return; 74 } 75 76 while (next_table->rodata_offset == orig_table->rodata_offset) { 77 next_table = list_next_entry(next_table, jump_info); 78 if (&next_table->jump_info == &table_list) { 79 *table_size = 0; 80 return; 81 } 82 } 83 84 *table_size = next_table->rodata_offset - orig_table->rodata_offset; 85 } 86 } 87 } 88 89 static struct reloc *find_reloc_by_table_annotate(struct objtool_file *file, 90 struct instruction *insn, 91 unsigned long *table_size) 92 { 93 struct section *rsec; 94 struct reloc *reloc; 95 unsigned long offset; 96 97 rsec = find_section_by_name(file->elf, ".rela.discard.tablejump_annotate"); 98 if (!rsec) 99 return NULL; 100 101 for_each_reloc(rsec, reloc) { 102 if (reloc->sym->sec->rodata) 103 continue; 104 105 if (strcmp(insn->sec->name, reloc->sym->sec->name)) 106 continue; 107 108 offset = reloc->sym->offset + reloc_addend(reloc); 109 if (insn->offset == offset) { 110 get_rodata_table_size_by_table_annotate(file, insn, table_size); 111 reloc++; 112 return reloc; 113 } 114 } 115 116 return NULL; 117 } 118 119 static struct reloc *find_reloc_of_rodata_c_jump_table(struct section *sec, 120 unsigned long offset, 121 unsigned long *table_size) 122 { 123 struct section *rsec; 124 struct reloc *reloc; 125 126 rsec = sec->rsec; 127 if (!rsec) 128 return NULL; 129 130 for_each_reloc(rsec, reloc) { 131 if (reloc_offset(reloc) > offset) 132 break; 133 134 if (!strcmp(reloc->sym->sec->name, C_JUMP_TABLE_SECTION)) { 135 *table_size = 0; 136 return reloc; 137 } 138 } 139 140 return NULL; 141 } 142 143 struct reloc *arch_find_switch_table(struct objtool_file *file, 144 struct instruction *insn, 145 unsigned long *table_size) 146 { 147 struct reloc *annotate_reloc; 148 struct reloc *rodata_reloc; 149 struct section *table_sec; 150 unsigned long table_offset; 151 152 annotate_reloc = find_reloc_by_table_annotate(file, insn, table_size); 153 if (!annotate_reloc) { 154 annotate_reloc = find_reloc_of_rodata_c_jump_table( 155 insn->sec, insn->offset, table_size); 156 if (!annotate_reloc) 157 return NULL; 158 } 159 160 table_sec = annotate_reloc->sym->sec; 161 table_offset = annotate_reloc->sym->offset + reloc_addend(annotate_reloc); 162 163 /* 164 * Each table entry has a rela associated with it. The rela 165 * should reference text in the same function as the original 166 * instruction. 167 */ 168 rodata_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset); 169 if (!rodata_reloc) 170 return NULL; 171 172 return rodata_reloc; 173 } 174