// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include 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; rsec = find_section_by_name(file->elf, ".rela.discard.tablejump_annotate"); if (!rsec) return; INIT_LIST_HEAD(&table_list); for_each_reloc(rsec, reloc) { 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; } 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; }