Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,3 @@
objtool-y += decode.o
objtool-y += special.o
objtool-y += orc.o
@@ -0,0 +1,439 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string.h>
#include <objtool/check.h>
#include <objtool/disas.h>
#include <objtool/warn.h>
#include <asm/inst.h>
#include <asm/orc_types.h>
#include <linux/objtool_types.h>
#include <arch/elf.h>
const char *arch_reg_name[CFI_NUM_REGS] = {
"zero", "ra", "tp", "sp",
"a0", "a1", "a2", "a3",
"a4", "a5", "a6", "a7",
"t0", "t1", "t2", "t3",
"t4", "t5", "t6", "t7",
"t8", "u0", "fp", "s0",
"s1", "s2", "s3", "s4",
"s5", "s6", "s7", "s8"
};
int arch_ftrace_match(const char *name)
{
return !strcmp(name, "_mcount");
}
unsigned long arch_jump_destination(struct instruction *insn)
{
return insn->offset + (insn->immediate << 2);
}
s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)
{
return reloc_addend(reloc);
}
bool arch_pc_relative_reloc(struct reloc *reloc)
{
return false;
}
bool arch_callee_saved_reg(unsigned char reg)
{
switch (reg) {
case CFI_RA:
case CFI_FP:
case CFI_S0 ... CFI_S8:
return true;
default:
return false;
}
}
int arch_decode_hint_reg(u8 sp_reg, int *base)
{
switch (sp_reg) {
case ORC_REG_UNDEFINED:
*base = CFI_UNDEFINED;
break;
case ORC_REG_SP:
*base = CFI_SP;
break;
case ORC_REG_FP:
*base = CFI_FP;
break;
default:
return -1;
}
return 0;
}
static bool is_loongarch(const struct elf *elf)
{
if (elf->ehdr.e_machine == EM_LOONGARCH)
return true;
ERROR("unexpected ELF machine type %d", elf->ehdr.e_machine);
return false;
}
#define ADD_OP(op) \
if (!(op = calloc(1, sizeof(*op)))) \
return -1; \
else for (*ops_list = op, ops_list = &op->next; op; op = NULL)
static bool decode_insn_reg0i26_fomat(union loongarch_instruction inst,
struct instruction *insn)
{
switch (inst.reg0i26_format.opcode) {
case b_op:
insn->type = INSN_JUMP_UNCONDITIONAL;
insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |
inst.reg0i26_format.immediate_l, 25);
break;
case bl_op:
insn->type = INSN_CALL;
insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |
inst.reg0i26_format.immediate_l, 25);
break;
default:
return false;
}
return true;
}
static bool decode_insn_reg1i21_fomat(union loongarch_instruction inst,
struct instruction *insn)
{
switch (inst.reg1i21_format.opcode) {
case beqz_op:
case bnez_op:
case bceqz_op:
insn->type = INSN_JUMP_CONDITIONAL;
insn->immediate = sign_extend64(inst.reg1i21_format.immediate_h << 16 |
inst.reg1i21_format.immediate_l, 20);
break;
default:
return false;
}
return true;
}
static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst,
struct instruction *insn,
struct stack_op **ops_list,
struct stack_op *op)
{
switch (inst.reg2i12_format.opcode) {
case addid_op:
if ((inst.reg2i12_format.rd == CFI_SP) || (inst.reg2i12_format.rj == CFI_SP)) {
/* addi.d sp,sp,si12 or addi.d fp,sp,si12 or addi.d sp,fp,si12 */
insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
ADD_OP(op) {
op->src.type = OP_SRC_ADD;
op->src.reg = inst.reg2i12_format.rj;
op->src.offset = insn->immediate;
op->dest.type = OP_DEST_REG;
op->dest.reg = inst.reg2i12_format.rd;
}
}
if ((inst.reg2i12_format.rd == CFI_SP) && (inst.reg2i12_format.rj == CFI_FP)) {
/* addi.d sp,fp,si12 */
struct symbol *func = find_func_containing(insn->sec, insn->offset);
if (!func)
return false;
func->frame_pointer = true;
}
break;
case ldd_op:
if (inst.reg2i12_format.rj == CFI_SP) {
/* ld.d rd,sp,si12 */
insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
ADD_OP(op) {
op->src.type = OP_SRC_REG_INDIRECT;
op->src.reg = CFI_SP;
op->src.offset = insn->immediate;
op->dest.type = OP_DEST_REG;
op->dest.reg = inst.reg2i12_format.rd;
}
}
break;
case std_op:
if (inst.reg2i12_format.rj == CFI_SP) {
/* st.d rd,sp,si12 */
insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
ADD_OP(op) {
op->src.type = OP_SRC_REG;
op->src.reg = inst.reg2i12_format.rd;
op->dest.type = OP_DEST_REG_INDIRECT;
op->dest.reg = CFI_SP;
op->dest.offset = insn->immediate;
}
}
break;
case andi_op:
if (inst.reg2i12_format.rd == 0 &&
inst.reg2i12_format.rj == 0 &&
inst.reg2i12_format.immediate == 0)
/* andi r0,r0,0 */
insn->type = INSN_NOP;
break;
default:
return false;
}
return true;
}
static bool decode_insn_reg2i14_fomat(union loongarch_instruction inst,
struct instruction *insn,
struct stack_op **ops_list,
struct stack_op *op)
{
switch (inst.reg2i14_format.opcode) {
case ldptrd_op:
if (inst.reg2i14_format.rj == CFI_SP) {
/* ldptr.d rd,sp,si14 */
insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);
ADD_OP(op) {
op->src.type = OP_SRC_REG_INDIRECT;
op->src.reg = CFI_SP;
op->src.offset = insn->immediate;
op->dest.type = OP_DEST_REG;
op->dest.reg = inst.reg2i14_format.rd;
}
}
break;
case stptrd_op:
if (inst.reg2i14_format.rj == CFI_SP) {
/* stptr.d ra,sp,0 */
if (inst.reg2i14_format.rd == LOONGARCH_GPR_RA &&
inst.reg2i14_format.immediate == 0)
break;
/* stptr.d rd,sp,si14 */
insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);
ADD_OP(op) {
op->src.type = OP_SRC_REG;
op->src.reg = inst.reg2i14_format.rd;
op->dest.type = OP_DEST_REG_INDIRECT;
op->dest.reg = CFI_SP;
op->dest.offset = insn->immediate;
}
}
break;
default:
return false;
}
return true;
}
static bool decode_insn_reg2i16_fomat(union loongarch_instruction inst,
struct instruction *insn)
{
switch (inst.reg2i16_format.opcode) {
case jirl_op:
if (inst.reg2i16_format.rd == 0 &&
inst.reg2i16_format.rj == CFI_RA &&
inst.reg2i16_format.immediate == 0) {
/* jirl r0,ra,0 */
insn->type = INSN_RETURN;
} else if (inst.reg2i16_format.rd == CFI_RA) {
/* jirl ra,rj,offs16 */
insn->type = INSN_CALL_DYNAMIC;
} else if (inst.reg2i16_format.rd == CFI_A0 &&
inst.reg2i16_format.immediate == 0) {
/*
* jirl a0,t0,0
* this is a special case in loongarch_suspend_enter,
* just treat it as a call instruction.
*/
insn->type = INSN_CALL_DYNAMIC;
} else if (inst.reg2i16_format.rd == 0 &&
inst.reg2i16_format.immediate == 0) {
/* jirl r0,rj,0 */
insn->type = INSN_JUMP_DYNAMIC;
} else if (inst.reg2i16_format.rd == 0 &&
inst.reg2i16_format.immediate != 0) {
/*
* jirl r0,t0,12
* this is a rare case in JUMP_VIRT_ADDR,
* just ignore it due to it is harmless for tracing.
*/
break;
} else {
/* jirl rd,rj,offs16 */
insn->type = INSN_JUMP_UNCONDITIONAL;
insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);
}
break;
case beq_op:
case bne_op:
case blt_op:
case bge_op:
case bltu_op:
case bgeu_op:
insn->type = INSN_JUMP_CONDITIONAL;
insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);
break;
default:
return false;
}
return true;
}
static bool decode_insn_reg3_fomat(union loongarch_instruction inst,
struct instruction *insn)
{
switch (inst.reg3_format.opcode) {
case amswapw_op:
if (inst.reg3_format.rd == LOONGARCH_GPR_ZERO &&
inst.reg3_format.rk == LOONGARCH_GPR_RA &&
inst.reg3_format.rj == LOONGARCH_GPR_ZERO) {
/* amswap.w $zero, $ra, $zero */
insn->type = INSN_BUG;
}
break;
default:
return false;
}
return true;
}
int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
unsigned long offset, unsigned int maxlen,
struct instruction *insn)
{
struct stack_op **ops_list = &insn->stack_ops;
const struct elf *elf = file->elf;
struct stack_op *op = NULL;
union loongarch_instruction inst;
if (!is_loongarch(elf))
return -1;
if (maxlen < LOONGARCH_INSN_SIZE)
return 0;
insn->len = LOONGARCH_INSN_SIZE;
insn->type = INSN_OTHER;
insn->immediate = 0;
inst = *(union loongarch_instruction *)(sec->data->d_buf + offset);
if (decode_insn_reg0i26_fomat(inst, insn))
return 0;
if (decode_insn_reg1i21_fomat(inst, insn))
return 0;
if (decode_insn_reg2i12_fomat(inst, insn, ops_list, op))
return 0;
if (decode_insn_reg2i14_fomat(inst, insn, ops_list, op))
return 0;
if (decode_insn_reg2i16_fomat(inst, insn))
return 0;
if (decode_insn_reg3_fomat(inst, insn))
return 0;
if (inst.word == 0) {
/* andi $zero, $zero, 0x0 */
insn->type = INSN_NOP;
} else if (inst.reg0i15_format.opcode == break_op &&
inst.reg0i15_format.immediate == 0x0) {
/* break 0x0 */
insn->type = INSN_TRAP;
} else if (inst.reg0i15_format.opcode == break_op &&
inst.reg0i15_format.immediate == 0x1) {
/* break 0x1 */
insn->type = INSN_BUG;
} else if (inst.reg2_format.opcode == ertn_op) {
/* ertn */
insn->type = INSN_RETURN;
}
return 0;
}
const char *arch_nop_insn(int len)
{
static u32 nop;
if (len != LOONGARCH_INSN_SIZE) {
ERROR("invalid NOP size: %d\n", len);
return NULL;
}
nop = LOONGARCH_INSN_NOP;
return (const char *)&nop;
}
const char *arch_ret_insn(int len)
{
static u32 ret;
if (len != LOONGARCH_INSN_SIZE) {
ERROR("invalid RET size: %d\n", len);
return NULL;
}
emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0);
return (const char *)&ret;
}
void arch_initial_func_cfi_state(struct cfi_init_state *state)
{
int i;
for (i = 0; i < CFI_NUM_REGS; i++) {
state->regs[i].base = CFI_UNDEFINED;
state->regs[i].offset = 0;
}
/* initial CFA (call frame address) */
state->cfa.base = CFI_SP;
state->cfa.offset = 0;
}
unsigned int arch_reloc_size(struct reloc *reloc)
{
switch (reloc_type(reloc)) {
case R_LARCH_32:
case R_LARCH_32_PCREL:
return 4;
default:
return 8;
}
}
unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table)
{
switch (reloc_type(reloc)) {
case R_LARCH_32_PCREL:
case R_LARCH_64_PCREL:
return reloc->sym->offset + reloc_addend(reloc) -
(reloc_offset(reloc) - reloc_offset(table));
default:
return reloc->sym->offset + reloc_addend(reloc);
}
}
#ifdef DISAS
int arch_disas_info_init(struct disassemble_info *dinfo)
{
return disas_info_init(dinfo, bfd_arch_loongarch,
bfd_mach_loongarch32, bfd_mach_loongarch64,
NULL);
}
#endif /* DISAS */
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _OBJTOOL_ARCH_CFI_REGS_H
#define _OBJTOOL_ARCH_CFI_REGS_H
#define CFI_RA 1
#define CFI_SP 3
#define CFI_A0 4
#define CFI_FP 22
#define CFI_S0 23
#define CFI_S1 24
#define CFI_S2 25
#define CFI_S3 26
#define CFI_S4 27
#define CFI_S5 28
#define CFI_S6 29
#define CFI_S7 30
#define CFI_S8 31
#define CFI_NUM_REGS 32
#define CFI_BP CFI_FP
#endif /* _OBJTOOL_ARCH_CFI_REGS_H */
@@ -0,0 +1,37 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _OBJTOOL_ARCH_ELF_H
#define _OBJTOOL_ARCH_ELF_H
/*
* See the following link for more info about ELF Relocation types:
* https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html#_relocations
*/
#ifndef R_LARCH_NONE
#define R_LARCH_NONE 0
#endif
#ifndef R_LARCH_32
#define R_LARCH_32 1
#endif
#ifndef R_LARCH_64
#define R_LARCH_64 2
#endif
#ifndef R_LARCH_32_PCREL
#define R_LARCH_32_PCREL 99
#endif
#ifndef R_LARCH_64_PCREL
#define R_LARCH_64_PCREL 109
#endif
#ifndef EM_LOONGARCH
#define EM_LOONGARCH 258
#endif
#define R_NONE R_LARCH_NONE
#define R_ABS32 R_LARCH_32
#define R_ABS64 R_LARCH_64
#define R_DATA32 R_LARCH_32_PCREL
#define R_DATA64 R_LARCH_32_PCREL
#define R_TEXT32 R_LARCH_32_PCREL
#define R_TEXT64 R_LARCH_32_PCREL
#endif /* _OBJTOOL_ARCH_ELF_H */
@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _OBJTOOL_ARCH_SPECIAL_H
#define _OBJTOOL_ARCH_SPECIAL_H
/*
* See more info about struct exception_table_entry
* in arch/loongarch/include/asm/extable.h
*/
#define EX_ENTRY_SIZE 12
#define EX_ORIG_OFFSET 0
#define EX_NEW_OFFSET 4
/*
* See more info about struct jump_entry
* in include/linux/jump_label.h
*/
#define JUMP_ENTRY_SIZE 16
#define JUMP_ORIG_OFFSET 0
#define JUMP_NEW_OFFSET 4
#define JUMP_KEY_OFFSET 8
/*
* See more info about struct alt_instr
* in arch/loongarch/include/asm/alternative.h
*/
#define ALT_ENTRY_SIZE 12
#define ALT_ORIG_OFFSET 0
#define ALT_NEW_OFFSET 4
#define ALT_FEATURE_OFFSET 8
#define ALT_ORIG_LEN_OFFSET 10
#define ALT_NEW_LEN_OFFSET 11
#endif /* _OBJTOOL_ARCH_SPECIAL_H */
@@ -0,0 +1,170 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/objtool_types.h>
#include <asm/orc_types.h>
#include <objtool/check.h>
#include <objtool/orc.h>
#include <objtool/warn.h>
int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn)
{
struct cfi_reg *fp = &cfi->regs[CFI_FP];
struct cfi_reg *ra = &cfi->regs[CFI_RA];
memset(orc, 0, sizeof(*orc));
if (!cfi) {
/*
* This is usually either unreachable nops/traps (which don't
* trigger unreachable instruction warnings), or
* STACK_FRAME_NON_STANDARD functions.
*/
orc->type = ORC_TYPE_UNDEFINED;
return 0;
}
switch (cfi->type) {
case UNWIND_HINT_TYPE_UNDEFINED:
orc->type = ORC_TYPE_UNDEFINED;
return 0;
case UNWIND_HINT_TYPE_END_OF_STACK:
orc->type = ORC_TYPE_END_OF_STACK;
return 0;
case UNWIND_HINT_TYPE_CALL:
orc->type = ORC_TYPE_CALL;
break;
case UNWIND_HINT_TYPE_REGS:
orc->type = ORC_TYPE_REGS;
break;
case UNWIND_HINT_TYPE_REGS_PARTIAL:
orc->type = ORC_TYPE_REGS_PARTIAL;
break;
default:
ERROR_INSN(insn, "unknown unwind hint type %d", cfi->type);
return -1;
}
orc->signal = cfi->signal;
switch (cfi->cfa.base) {
case CFI_SP:
orc->sp_reg = ORC_REG_SP;
break;
case CFI_FP:
orc->sp_reg = ORC_REG_FP;
break;
default:
ERROR_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base);
return -1;
}
switch (fp->base) {
case CFI_UNDEFINED:
orc->fp_reg = ORC_REG_UNDEFINED;
orc->fp_offset = 0;
break;
case CFI_CFA:
orc->fp_reg = ORC_REG_PREV_SP;
orc->fp_offset = fp->offset;
break;
case CFI_FP:
orc->fp_reg = ORC_REG_FP;
break;
default:
ERROR_INSN(insn, "unknown FP base reg %d", fp->base);
return -1;
}
switch (ra->base) {
case CFI_UNDEFINED:
orc->ra_reg = ORC_REG_UNDEFINED;
orc->ra_offset = 0;
break;
case CFI_CFA:
orc->ra_reg = ORC_REG_PREV_SP;
orc->ra_offset = ra->offset;
break;
case CFI_FP:
orc->ra_reg = ORC_REG_FP;
break;
default:
ERROR_INSN(insn, "unknown RA base reg %d", ra->base);
return -1;
}
orc->sp_offset = cfi->cfa.offset;
return 0;
}
int write_orc_entry(struct elf *elf, struct section *orc_sec,
struct section *ip_sec, unsigned int idx,
struct section *insn_sec, unsigned long insn_off,
struct orc_entry *o)
{
struct orc_entry *orc;
/* populate ORC data */
orc = (struct orc_entry *)orc_sec->data->d_buf + idx;
memcpy(orc, o, sizeof(*orc));
/* populate reloc for ip */
if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx,
insn_sec, insn_off))
return -1;
return 0;
}
static const char *reg_name(unsigned int reg)
{
switch (reg) {
case ORC_REG_SP:
return "sp";
case ORC_REG_FP:
return "fp";
case ORC_REG_PREV_SP:
return "prevsp";
default:
return "?";
}
}
static const char *orc_type_name(unsigned int type)
{
switch (type) {
case UNWIND_HINT_TYPE_CALL:
return "call";
case UNWIND_HINT_TYPE_REGS:
return "regs";
case UNWIND_HINT_TYPE_REGS_PARTIAL:
return "regs (partial)";
default:
return "?";
}
}
static void print_reg(unsigned int reg, int offset)
{
if (reg == ORC_REG_UNDEFINED)
printf(" (und) ");
else
printf("%s + %3d", reg_name(reg), offset);
}
void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i)
{
printf("type:%s", orc_type_name(orc[i].type));
printf(" sp:");
print_reg(orc[i].sp_reg, orc[i].sp_offset);
printf(" fp:");
print_reg(orc[i].fp_reg, orc[i].fp_offset);
printf(" ra:");
print_reg(orc[i].ra_reg, orc[i].ra_offset);
printf(" signal:%d\n", orc[i].signal);
}
@@ -0,0 +1,201 @@
// 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;
}