0001
0002
0003
0004
0005
0006 #include <stdlib.h>
0007 #include <string.h>
0008
0009 #include <linux/objtool.h>
0010 #include <asm/orc_types.h>
0011
0012 #include <objtool/check.h>
0013 #include <objtool/warn.h>
0014 #include <objtool/endianness.h>
0015
0016 static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi,
0017 struct instruction *insn)
0018 {
0019 struct cfi_reg *bp = &cfi->regs[CFI_BP];
0020
0021 memset(orc, 0, sizeof(*orc));
0022
0023 if (!cfi) {
0024 orc->end = 0;
0025 orc->sp_reg = ORC_REG_UNDEFINED;
0026 return 0;
0027 }
0028
0029 orc->end = cfi->end;
0030
0031 if (cfi->cfa.base == CFI_UNDEFINED) {
0032 orc->sp_reg = ORC_REG_UNDEFINED;
0033 return 0;
0034 }
0035
0036 switch (cfi->cfa.base) {
0037 case CFI_SP:
0038 orc->sp_reg = ORC_REG_SP;
0039 break;
0040 case CFI_SP_INDIRECT:
0041 orc->sp_reg = ORC_REG_SP_INDIRECT;
0042 break;
0043 case CFI_BP:
0044 orc->sp_reg = ORC_REG_BP;
0045 break;
0046 case CFI_BP_INDIRECT:
0047 orc->sp_reg = ORC_REG_BP_INDIRECT;
0048 break;
0049 case CFI_R10:
0050 orc->sp_reg = ORC_REG_R10;
0051 break;
0052 case CFI_R13:
0053 orc->sp_reg = ORC_REG_R13;
0054 break;
0055 case CFI_DI:
0056 orc->sp_reg = ORC_REG_DI;
0057 break;
0058 case CFI_DX:
0059 orc->sp_reg = ORC_REG_DX;
0060 break;
0061 default:
0062 WARN_FUNC("unknown CFA base reg %d",
0063 insn->sec, insn->offset, cfi->cfa.base);
0064 return -1;
0065 }
0066
0067 switch (bp->base) {
0068 case CFI_UNDEFINED:
0069 orc->bp_reg = ORC_REG_UNDEFINED;
0070 break;
0071 case CFI_CFA:
0072 orc->bp_reg = ORC_REG_PREV_SP;
0073 break;
0074 case CFI_BP:
0075 orc->bp_reg = ORC_REG_BP;
0076 break;
0077 default:
0078 WARN_FUNC("unknown BP base reg %d",
0079 insn->sec, insn->offset, bp->base);
0080 return -1;
0081 }
0082
0083 orc->sp_offset = cfi->cfa.offset;
0084 orc->bp_offset = bp->offset;
0085 orc->type = cfi->type;
0086
0087 return 0;
0088 }
0089
0090 static int write_orc_entry(struct elf *elf, struct section *orc_sec,
0091 struct section *ip_sec, unsigned int idx,
0092 struct section *insn_sec, unsigned long insn_off,
0093 struct orc_entry *o)
0094 {
0095 struct orc_entry *orc;
0096
0097
0098 orc = (struct orc_entry *)orc_sec->data->d_buf + idx;
0099 memcpy(orc, o, sizeof(*orc));
0100 orc->sp_offset = bswap_if_needed(orc->sp_offset);
0101 orc->bp_offset = bswap_if_needed(orc->bp_offset);
0102
0103
0104 if (elf_add_reloc_to_insn(elf, ip_sec, idx * sizeof(int), R_X86_64_PC32,
0105 insn_sec, insn_off))
0106 return -1;
0107
0108 return 0;
0109 }
0110
0111 struct orc_list_entry {
0112 struct list_head list;
0113 struct orc_entry orc;
0114 struct section *insn_sec;
0115 unsigned long insn_off;
0116 };
0117
0118 static int orc_list_add(struct list_head *orc_list, struct orc_entry *orc,
0119 struct section *sec, unsigned long offset)
0120 {
0121 struct orc_list_entry *entry = malloc(sizeof(*entry));
0122
0123 if (!entry) {
0124 WARN("malloc failed");
0125 return -1;
0126 }
0127
0128 entry->orc = *orc;
0129 entry->insn_sec = sec;
0130 entry->insn_off = offset;
0131
0132 list_add_tail(&entry->list, orc_list);
0133 return 0;
0134 }
0135
0136 static unsigned long alt_group_len(struct alt_group *alt_group)
0137 {
0138 return alt_group->last_insn->offset +
0139 alt_group->last_insn->len -
0140 alt_group->first_insn->offset;
0141 }
0142
0143 int orc_create(struct objtool_file *file)
0144 {
0145 struct section *sec, *orc_sec;
0146 unsigned int nr = 0, idx = 0;
0147 struct orc_list_entry *entry;
0148 struct list_head orc_list;
0149
0150 struct orc_entry null = {
0151 .sp_reg = ORC_REG_UNDEFINED,
0152 .bp_reg = ORC_REG_UNDEFINED,
0153 .type = UNWIND_HINT_TYPE_CALL,
0154 };
0155
0156
0157 INIT_LIST_HEAD(&orc_list);
0158 for_each_sec(file, sec) {
0159 struct orc_entry orc, prev_orc = {0};
0160 struct instruction *insn;
0161 bool empty = true;
0162
0163 if (!sec->text)
0164 continue;
0165
0166 sec_for_each_insn(file, sec, insn) {
0167 struct alt_group *alt_group = insn->alt_group;
0168 int i;
0169
0170 if (!alt_group) {
0171 if (init_orc_entry(&orc, insn->cfi, insn))
0172 return -1;
0173 if (!memcmp(&prev_orc, &orc, sizeof(orc)))
0174 continue;
0175 if (orc_list_add(&orc_list, &orc, sec,
0176 insn->offset))
0177 return -1;
0178 nr++;
0179 prev_orc = orc;
0180 empty = false;
0181 continue;
0182 }
0183
0184
0185
0186
0187
0188
0189
0190
0191 for (i = 0; i < alt_group_len(alt_group); i++) {
0192 struct cfi_state *cfi = alt_group->cfi[i];
0193 if (!cfi)
0194 continue;
0195
0196 if (init_orc_entry(&orc, cfi, insn))
0197 return -1;
0198 if (!memcmp(&prev_orc, &orc, sizeof(orc)))
0199 continue;
0200 if (orc_list_add(&orc_list, &orc, insn->sec,
0201 insn->offset + i))
0202 return -1;
0203 nr++;
0204 prev_orc = orc;
0205 empty = false;
0206 }
0207
0208
0209 insn = alt_group->last_insn;
0210 }
0211
0212
0213 if (!empty) {
0214 orc_list_add(&orc_list, &null, sec, sec->sh.sh_size);
0215 nr++;
0216 }
0217 }
0218 if (!nr)
0219 return 0;
0220
0221
0222 sec = find_section_by_name(file->elf, ".orc_unwind");
0223 if (sec) {
0224 WARN("file already has .orc_unwind section, skipping");
0225 return -1;
0226 }
0227 orc_sec = elf_create_section(file->elf, ".orc_unwind", 0,
0228 sizeof(struct orc_entry), nr);
0229 if (!orc_sec)
0230 return -1;
0231
0232 sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), nr);
0233 if (!sec)
0234 return -1;
0235
0236
0237 list_for_each_entry(entry, &orc_list, list) {
0238 if (write_orc_entry(file->elf, orc_sec, sec, idx++,
0239 entry->insn_sec, entry->insn_off,
0240 &entry->orc))
0241 return -1;
0242 }
0243
0244 return 0;
0245 }