Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
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     /* populate ORC data */
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     /* populate reloc for ip */
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     /* Build a deduplicated list of ORC entries: */
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              * Alternatives can have different stack layout
0186              * possibilities (but they shouldn't conflict).
0187              * Instead of traversing the instructions, use the
0188              * alt_group's flattened byte-offset-addressed CFI
0189              * array.
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                 /* errors are reported on the original insn */
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             /* Skip to the end of the alt_group */
0209             insn = alt_group->last_insn;
0210         }
0211 
0212         /* Add a section terminator */
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     /* Create .orc_unwind, .orc_unwind_ip and .rela.orc_unwind_ip sections: */
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     /* Write ORC entries to sections: */
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 }