| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- // 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;
- }
|