174 lines
4.3 KiB
C
174 lines
4.3 KiB
C
// 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;
|
|
|
|
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;
|
|
}
|