| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- // 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);
- }
|