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