1 // SPDX-License-Identifier: GPL-2.0-or-later 2 #include <linux/objtool_types.h> 3 #include <asm/orc_types.h> 4 5 #include <objtool/check.h> 6 #include <objtool/orc.h> 7 #include <objtool/warn.h> 8 #include <objtool/endianness.h> 9 10 int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn) 11 { 12 struct cfi_reg *fp = &cfi->regs[CFI_FP]; 13 struct cfi_reg *ra = &cfi->regs[CFI_RA]; 14 15 memset(orc, 0, sizeof(*orc)); 16 17 if (!cfi) { 18 /* 19 * This is usually either unreachable nops/traps (which don't 20 * trigger unreachable instruction warnings), or 21 * STACK_FRAME_NON_STANDARD functions. 22 */ 23 orc->type = ORC_TYPE_UNDEFINED; 24 return 0; 25 } 26 27 switch (cfi->type) { 28 case UNWIND_HINT_TYPE_UNDEFINED: 29 orc->type = ORC_TYPE_UNDEFINED; 30 return 0; 31 case UNWIND_HINT_TYPE_END_OF_STACK: 32 orc->type = ORC_TYPE_END_OF_STACK; 33 return 0; 34 case UNWIND_HINT_TYPE_CALL: 35 orc->type = ORC_TYPE_CALL; 36 break; 37 case UNWIND_HINT_TYPE_REGS: 38 orc->type = ORC_TYPE_REGS; 39 break; 40 case UNWIND_HINT_TYPE_REGS_PARTIAL: 41 orc->type = ORC_TYPE_REGS_PARTIAL; 42 break; 43 default: 44 WARN_INSN(insn, "unknown unwind hint type %d", cfi->type); 45 return -1; 46 } 47 48 orc->signal = cfi->signal; 49 50 switch (cfi->cfa.base) { 51 case CFI_SP: 52 orc->sp_reg = ORC_REG_SP; 53 break; 54 case CFI_FP: 55 orc->sp_reg = ORC_REG_FP; 56 break; 57 default: 58 WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base); 59 return -1; 60 } 61 62 switch (fp->base) { 63 case CFI_UNDEFINED: 64 orc->fp_reg = ORC_REG_UNDEFINED; 65 orc->fp_offset = 0; 66 break; 67 case CFI_CFA: 68 orc->fp_reg = ORC_REG_PREV_SP; 69 orc->fp_offset = fp->offset; 70 break; 71 case CFI_FP: 72 orc->fp_reg = ORC_REG_FP; 73 break; 74 default: 75 WARN_INSN(insn, "unknown FP base reg %d", fp->base); 76 return -1; 77 } 78 79 switch (ra->base) { 80 case CFI_UNDEFINED: 81 orc->ra_reg = ORC_REG_UNDEFINED; 82 orc->ra_offset = 0; 83 break; 84 case CFI_CFA: 85 orc->ra_reg = ORC_REG_PREV_SP; 86 orc->ra_offset = ra->offset; 87 break; 88 case CFI_FP: 89 orc->ra_reg = ORC_REG_FP; 90 break; 91 default: 92 WARN_INSN(insn, "unknown RA base reg %d", ra->base); 93 return -1; 94 } 95 96 orc->sp_offset = cfi->cfa.offset; 97 98 return 0; 99 } 100 101 int write_orc_entry(struct elf *elf, struct section *orc_sec, 102 struct section *ip_sec, unsigned int idx, 103 struct section *insn_sec, unsigned long insn_off, 104 struct orc_entry *o) 105 { 106 struct orc_entry *orc; 107 108 /* populate ORC data */ 109 orc = (struct orc_entry *)orc_sec->data->d_buf + idx; 110 memcpy(orc, o, sizeof(*orc)); 111 112 /* populate reloc for ip */ 113 if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx, 114 insn_sec, insn_off)) 115 return -1; 116 117 return 0; 118 } 119 120 static const char *reg_name(unsigned int reg) 121 { 122 switch (reg) { 123 case ORC_REG_SP: 124 return "sp"; 125 case ORC_REG_FP: 126 return "fp"; 127 case ORC_REG_PREV_SP: 128 return "prevsp"; 129 default: 130 return "?"; 131 } 132 } 133 134 static const char *orc_type_name(unsigned int type) 135 { 136 switch (type) { 137 case UNWIND_HINT_TYPE_CALL: 138 return "call"; 139 case UNWIND_HINT_TYPE_REGS: 140 return "regs"; 141 case UNWIND_HINT_TYPE_REGS_PARTIAL: 142 return "regs (partial)"; 143 default: 144 return "?"; 145 } 146 } 147 148 static void print_reg(unsigned int reg, int offset) 149 { 150 if (reg == ORC_REG_UNDEFINED) 151 printf(" (und) "); 152 else 153 printf("%s + %3d", reg_name(reg), offset); 154 155 } 156 157 void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i) 158 { 159 printf("type:%s", orc_type_name(orc[i].type)); 160 161 printf(" sp:"); 162 print_reg(orc[i].sp_reg, orc[i].sp_offset); 163 164 printf(" fp:"); 165 print_reg(orc[i].fp_reg, orc[i].fp_offset); 166 167 printf(" ra:"); 168 print_reg(orc[i].ra_reg, orc[i].ra_offset); 169 170 printf(" signal:%d\n", orc[i].signal); 171 } 172
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.