| // SPDX-License-Identifier: GPL-2.0-or-later | |
| #include <string.h> | |
| #include <objtool/special.h> | |
| #include <objtool/warn.h> | |
| bool arch_support_alt_relocation(struct special_alt *special_alt, | |
| struct instruction *insn, | |
| struct reloc *reloc) | |
| { | |
| return false; | |
| } | |
| struct table_info { | |
| struct list_head jump_info; | |
| unsigned long insn_offset; | |
| unsigned long rodata_offset; | |
| }; | |
| static void get_rodata_table_size_by_table_annotate(struct objtool_file *file, | |
| struct instruction *insn, | |
| unsigned long *table_size) | |
| { | |
| struct section *rsec; | |
| struct reloc *reloc; | |
| struct list_head table_list; | |
| struct table_info *orig_table; | |
| struct table_info *next_table; | |
| unsigned long tmp_insn_offset; | |
| unsigned long tmp_rodata_offset; | |
| bool is_valid_list = false; | |
| rsec = find_section_by_name(file->elf, ".rela.discard.tablejump_annotate"); | |
| if (!rsec) | |
| return; | |
| INIT_LIST_HEAD(&table_list); | |
| for_each_reloc(rsec, reloc) { | |
| if (reloc->sym->sec->rodata) | |
| continue; | |
| if (strcmp(insn->sec->name, reloc->sym->sec->name)) | |
| continue; | |
| orig_table = malloc(sizeof(struct table_info)); | |
| if (!orig_table) { | |
| WARN("malloc failed"); | |
| return; | |
| } | |
| orig_table->insn_offset = reloc->sym->offset + reloc_addend(reloc); | |
| reloc++; | |
| orig_table->rodata_offset = reloc->sym->offset + reloc_addend(reloc); | |
| list_add_tail(&orig_table->jump_info, &table_list); | |
| if (reloc_idx(reloc) + 1 == sec_num_entries(rsec)) | |
| break; | |
| if (strcmp(insn->sec->name, (reloc + 1)->sym->sec->name)) { | |
| list_for_each_entry(orig_table, &table_list, jump_info) { | |
| if (orig_table->insn_offset == insn->offset) { | |
| is_valid_list = true; | |
| break; | |
| } | |
| } | |
| if (!is_valid_list) { | |
| list_del_init(&table_list); | |
| continue; | |
| } | |
| break; | |
| } | |
| } | |
| list_for_each_entry(orig_table, &table_list, jump_info) { | |
| next_table = list_next_entry(orig_table, jump_info); | |
| list_for_each_entry_from(next_table, &table_list, jump_info) { | |
| if (next_table->rodata_offset < orig_table->rodata_offset) { | |
| tmp_insn_offset = next_table->insn_offset; | |
| tmp_rodata_offset = next_table->rodata_offset; | |
| next_table->insn_offset = orig_table->insn_offset; | |
| next_table->rodata_offset = orig_table->rodata_offset; | |
| orig_table->insn_offset = tmp_insn_offset; | |
| orig_table->rodata_offset = tmp_rodata_offset; | |
| } | |
| } | |
| } | |
| list_for_each_entry(orig_table, &table_list, jump_info) { | |
| if (insn->offset == orig_table->insn_offset) { | |
| next_table = list_next_entry(orig_table, jump_info); | |
| if (&next_table->jump_info == &table_list) { | |
| *table_size = 0; | |
| return; | |
| } | |
| while (next_table->rodata_offset == orig_table->rodata_offset) { | |
| next_table = list_next_entry(next_table, jump_info); | |
| if (&next_table->jump_info == &table_list) { | |
| *table_size = 0; | |
| return; | |
| } | |
| } | |
| *table_size = next_table->rodata_offset - orig_table->rodata_offset; | |
| } | |
| } | |
| } | |
| static struct reloc *find_reloc_by_table_annotate(struct objtool_file *file, | |
| struct instruction *insn, | |
| unsigned long *table_size) | |
| { | |
| struct section *rsec; | |
| struct reloc *reloc; | |
| unsigned long offset; | |
| rsec = find_section_by_name(file->elf, ".rela.discard.tablejump_annotate"); | |
| if (!rsec) | |
| return NULL; | |
| for_each_reloc(rsec, reloc) { | |
| if (reloc->sym->sec->rodata) | |
| continue; | |
| if (strcmp(insn->sec->name, reloc->sym->sec->name)) | |
| continue; | |
| offset = reloc->sym->offset + reloc_addend(reloc); | |
| if (insn->offset == offset) { | |
| get_rodata_table_size_by_table_annotate(file, insn, table_size); | |
| reloc++; | |
| return reloc; | |
| } | |
| } | |
| return NULL; | |
| } | |
| static struct reloc *find_reloc_of_rodata_c_jump_table(struct section *sec, | |
| unsigned long offset, | |
| unsigned long *table_size) | |
| { | |
| struct section *rsec; | |
| struct reloc *reloc; | |
| rsec = sec->rsec; | |
| if (!rsec) | |
| return NULL; | |
| for_each_reloc(rsec, reloc) { | |
| if (reloc_offset(reloc) > offset) | |
| break; | |
| if (!strcmp(reloc->sym->sec->name, C_JUMP_TABLE_SECTION)) { | |
| *table_size = 0; | |
| return reloc; | |
| } | |
| } | |
| return NULL; | |
| } | |
| struct reloc *arch_find_switch_table(struct objtool_file *file, | |
| struct instruction *insn, | |
| unsigned long *table_size) | |
| { | |
| struct reloc *annotate_reloc; | |
| struct reloc *rodata_reloc; | |
| struct section *table_sec; | |
| unsigned long table_offset; | |
| annotate_reloc = find_reloc_by_table_annotate(file, insn, table_size); | |
| if (!annotate_reloc) { | |
| annotate_reloc = find_reloc_of_rodata_c_jump_table( | |
| insn->sec, insn->offset, table_size); | |
| if (!annotate_reloc) | |
| return NULL; | |
| } | |
| table_sec = annotate_reloc->sym->sec; | |
| table_offset = annotate_reloc->sym->offset + reloc_addend(annotate_reloc); | |
| /* | |
| * Each table entry has a rela associated with it. The rela | |
| * should reference text in the same function as the original | |
| * instruction. | |
| */ | |
| rodata_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset); | |
| if (!rodata_reloc) | |
| return NULL; | |
| return rodata_reloc; | |
| } | |
| const char *arch_cpu_feature_name(int feature_number) | |
| { | |
| return NULL; | |
| } |