linux/tools/objtool/orc_gen.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
   3 *
   4 * This program is free software; you can redistribute it and/or
   5 * modify it under the terms of the GNU General Public License
   6 * as published by the Free Software Foundation; either version 2
   7 * of the License, or (at your option) any later version.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
  16 */
  17
  18#include <stdlib.h>
  19#include <string.h>
  20
  21#include "orc.h"
  22#include "check.h"
  23#include "warn.h"
  24
  25int create_orc(struct objtool_file *file)
  26{
  27        struct instruction *insn;
  28
  29        for_each_insn(file, insn) {
  30                struct orc_entry *orc = &insn->orc;
  31                struct cfi_reg *cfa = &insn->state.cfa;
  32                struct cfi_reg *bp = &insn->state.regs[CFI_BP];
  33
  34                if (cfa->base == CFI_UNDEFINED) {
  35                        orc->sp_reg = ORC_REG_UNDEFINED;
  36                        continue;
  37                }
  38
  39                switch (cfa->base) {
  40                case CFI_SP:
  41                        orc->sp_reg = ORC_REG_SP;
  42                        break;
  43                case CFI_SP_INDIRECT:
  44                        orc->sp_reg = ORC_REG_SP_INDIRECT;
  45                        break;
  46                case CFI_BP:
  47                        orc->sp_reg = ORC_REG_BP;
  48                        break;
  49                case CFI_BP_INDIRECT:
  50                        orc->sp_reg = ORC_REG_BP_INDIRECT;
  51                        break;
  52                case CFI_R10:
  53                        orc->sp_reg = ORC_REG_R10;
  54                        break;
  55                case CFI_R13:
  56                        orc->sp_reg = ORC_REG_R13;
  57                        break;
  58                case CFI_DI:
  59                        orc->sp_reg = ORC_REG_DI;
  60                        break;
  61                case CFI_DX:
  62                        orc->sp_reg = ORC_REG_DX;
  63                        break;
  64                default:
  65                        WARN_FUNC("unknown CFA base reg %d",
  66                                  insn->sec, insn->offset, cfa->base);
  67                        return -1;
  68                }
  69
  70                switch(bp->base) {
  71                case CFI_UNDEFINED:
  72                        orc->bp_reg = ORC_REG_UNDEFINED;
  73                        break;
  74                case CFI_CFA:
  75                        orc->bp_reg = ORC_REG_PREV_SP;
  76                        break;
  77                case CFI_BP:
  78                        orc->bp_reg = ORC_REG_BP;
  79                        break;
  80                default:
  81                        WARN_FUNC("unknown BP base reg %d",
  82                                  insn->sec, insn->offset, bp->base);
  83                        return -1;
  84                }
  85
  86                orc->sp_offset = cfa->offset;
  87                orc->bp_offset = bp->offset;
  88                orc->type = insn->state.type;
  89        }
  90
  91        return 0;
  92}
  93
  94static int create_orc_entry(struct section *u_sec, struct section *ip_relasec,
  95                                unsigned int idx, struct section *insn_sec,
  96                                unsigned long insn_off, struct orc_entry *o)
  97{
  98        struct orc_entry *orc;
  99        struct rela *rela;
 100
 101        if (!insn_sec->sym) {
 102                WARN("missing symbol for section %s", insn_sec->name);
 103                return -1;
 104        }
 105
 106        /* populate ORC data */
 107        orc = (struct orc_entry *)u_sec->data->d_buf + idx;
 108        memcpy(orc, o, sizeof(*orc));
 109
 110        /* populate rela for ip */
 111        rela = malloc(sizeof(*rela));
 112        if (!rela) {
 113                perror("malloc");
 114                return -1;
 115        }
 116        memset(rela, 0, sizeof(*rela));
 117
 118        rela->sym = insn_sec->sym;
 119        rela->addend = insn_off;
 120        rela->type = R_X86_64_PC32;
 121        rela->offset = idx * sizeof(int);
 122
 123        list_add_tail(&rela->list, &ip_relasec->rela_list);
 124        hash_add(ip_relasec->rela_hash, &rela->hash, rela->offset);
 125
 126        return 0;
 127}
 128
 129int create_orc_sections(struct objtool_file *file)
 130{
 131        struct instruction *insn, *prev_insn;
 132        struct section *sec, *u_sec, *ip_relasec;
 133        unsigned int idx;
 134
 135        struct orc_entry empty = {
 136                .sp_reg = ORC_REG_UNDEFINED,
 137                .bp_reg  = ORC_REG_UNDEFINED,
 138                .type    = ORC_TYPE_CALL,
 139        };
 140
 141        sec = find_section_by_name(file->elf, ".orc_unwind");
 142        if (sec) {
 143                WARN("file already has .orc_unwind section, skipping");
 144                return -1;
 145        }
 146
 147        /* count the number of needed orcs */
 148        idx = 0;
 149        for_each_sec(file, sec) {
 150                if (!sec->text)
 151                        continue;
 152
 153                prev_insn = NULL;
 154                sec_for_each_insn(file, sec, insn) {
 155                        if (!prev_insn ||
 156                            memcmp(&insn->orc, &prev_insn->orc,
 157                                   sizeof(struct orc_entry))) {
 158                                idx++;
 159                        }
 160                        prev_insn = insn;
 161                }
 162
 163                /* section terminator */
 164                if (prev_insn)
 165                        idx++;
 166        }
 167        if (!idx)
 168                return -1;
 169
 170
 171        /* create .orc_unwind_ip and .rela.orc_unwind_ip sections */
 172        sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx);
 173        if (!sec)
 174                return -1;
 175
 176        ip_relasec = elf_create_rela_section(file->elf, sec);
 177        if (!ip_relasec)
 178                return -1;
 179
 180        /* create .orc_unwind section */
 181        u_sec = elf_create_section(file->elf, ".orc_unwind",
 182                                   sizeof(struct orc_entry), idx);
 183
 184        /* populate sections */
 185        idx = 0;
 186        for_each_sec(file, sec) {
 187                if (!sec->text)
 188                        continue;
 189
 190                prev_insn = NULL;
 191                sec_for_each_insn(file, sec, insn) {
 192                        if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc,
 193                                                 sizeof(struct orc_entry))) {
 194
 195                                if (create_orc_entry(u_sec, ip_relasec, idx,
 196                                                     insn->sec, insn->offset,
 197                                                     &insn->orc))
 198                                        return -1;
 199
 200                                idx++;
 201                        }
 202                        prev_insn = insn;
 203                }
 204
 205                /* section terminator */
 206                if (prev_insn) {
 207                        if (create_orc_entry(u_sec, ip_relasec, idx,
 208                                             prev_insn->sec,
 209                                             prev_insn->offset + prev_insn->len,
 210                                             &empty))
 211                                return -1;
 212
 213                        idx++;
 214                }
 215        }
 216
 217        if (elf_rebuild_rela_section(ip_relasec))
 218                return -1;
 219
 220        return 0;
 221}
 222