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 <linux/objtool.h>
  10#include <asm/orc_types.h>
  11
  12#include "check.h"
  13#include "warn.h"
  14
  15int create_orc(struct objtool_file *file)
  16{
  17        struct instruction *insn;
  18
  19        for_each_insn(file, insn) {
  20                struct orc_entry *orc = &insn->orc;
  21                struct cfi_reg *cfa = &insn->cfi.cfa;
  22                struct cfi_reg *bp = &insn->cfi.regs[CFI_BP];
  23
  24                if (!insn->sec->text)
  25                        continue;
  26
  27                orc->end = insn->cfi.end;
  28
  29                if (cfa->base == CFI_UNDEFINED) {
  30                        orc->sp_reg = ORC_REG_UNDEFINED;
  31                        continue;
  32                }
  33
  34                switch (cfa->base) {
  35                case CFI_SP:
  36                        orc->sp_reg = ORC_REG_SP;
  37                        break;
  38                case CFI_SP_INDIRECT:
  39                        orc->sp_reg = ORC_REG_SP_INDIRECT;
  40                        break;
  41                case CFI_BP:
  42                        orc->sp_reg = ORC_REG_BP;
  43                        break;
  44                case CFI_BP_INDIRECT:
  45                        orc->sp_reg = ORC_REG_BP_INDIRECT;
  46                        break;
  47                case CFI_R10:
  48                        orc->sp_reg = ORC_REG_R10;
  49                        break;
  50                case CFI_R13:
  51                        orc->sp_reg = ORC_REG_R13;
  52                        break;
  53                case CFI_DI:
  54                        orc->sp_reg = ORC_REG_DI;
  55                        break;
  56                case CFI_DX:
  57                        orc->sp_reg = ORC_REG_DX;
  58                        break;
  59                default:
  60                        WARN_FUNC("unknown CFA base reg %d",
  61                                  insn->sec, insn->offset, cfa->base);
  62                        return -1;
  63                }
  64
  65                switch(bp->base) {
  66                case CFI_UNDEFINED:
  67                        orc->bp_reg = ORC_REG_UNDEFINED;
  68                        break;
  69                case CFI_CFA:
  70                        orc->bp_reg = ORC_REG_PREV_SP;
  71                        break;
  72                case CFI_BP:
  73                        orc->bp_reg = ORC_REG_BP;
  74                        break;
  75                default:
  76                        WARN_FUNC("unknown BP base reg %d",
  77                                  insn->sec, insn->offset, bp->base);
  78                        return -1;
  79                }
  80
  81                orc->sp_offset = cfa->offset;
  82                orc->bp_offset = bp->offset;
  83                orc->type = insn->cfi.type;
  84        }
  85
  86        return 0;
  87}
  88
  89static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relocsec,
  90                                unsigned int idx, struct section *insn_sec,
  91                                unsigned long insn_off, struct orc_entry *o)
  92{
  93        struct orc_entry *orc;
  94        struct reloc *reloc;
  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 reloc for ip */
 101        reloc = malloc(sizeof(*reloc));
 102        if (!reloc) {
 103                perror("malloc");
 104                return -1;
 105        }
 106        memset(reloc, 0, sizeof(*reloc));
 107
 108        if (insn_sec->sym) {
 109                reloc->sym = insn_sec->sym;
 110                reloc->addend = insn_off;
 111        } else {
 112                /*
 113                 * The Clang assembler doesn't produce section symbols, so we
 114                 * have to reference the function symbol instead:
 115                 */
 116                reloc->sym = find_symbol_containing(insn_sec, insn_off);
 117                if (!reloc->sym) {
 118                        /*
 119                         * Hack alert.  This happens when we need to reference
 120                         * the NOP pad insn immediately after the function.
 121                         */
 122                        reloc->sym = find_symbol_containing(insn_sec,
 123                                                           insn_off - 1);
 124                }
 125                if (!reloc->sym) {
 126                        WARN("missing symbol for insn at offset 0x%lx\n",
 127                             insn_off);
 128                        return -1;
 129                }
 130
 131                reloc->addend = insn_off - reloc->sym->offset;
 132        }
 133
 134        reloc->type = R_X86_64_PC32;
 135        reloc->offset = idx * sizeof(int);
 136        reloc->sec = ip_relocsec;
 137
 138        elf_add_reloc(elf, reloc);
 139
 140        return 0;
 141}
 142
 143int create_orc_sections(struct objtool_file *file)
 144{
 145        struct instruction *insn, *prev_insn;
 146        struct section *sec, *u_sec, *ip_relocsec;
 147        unsigned int idx;
 148
 149        struct orc_entry empty = {
 150                .sp_reg = ORC_REG_UNDEFINED,
 151                .bp_reg  = ORC_REG_UNDEFINED,
 152                .type    = UNWIND_HINT_TYPE_CALL,
 153        };
 154
 155        sec = find_section_by_name(file->elf, ".orc_unwind");
 156        if (sec) {
 157                WARN("file already has .orc_unwind section, skipping");
 158                return -1;
 159        }
 160
 161        /* count the number of needed orcs */
 162        idx = 0;
 163        for_each_sec(file, sec) {
 164                if (!sec->text)
 165                        continue;
 166
 167                prev_insn = NULL;
 168                sec_for_each_insn(file, sec, insn) {
 169                        if (!prev_insn ||
 170                            memcmp(&insn->orc, &prev_insn->orc,
 171                                   sizeof(struct orc_entry))) {
 172                                idx++;
 173                        }
 174                        prev_insn = insn;
 175                }
 176
 177                /* section terminator */
 178                if (prev_insn)
 179                        idx++;
 180        }
 181        if (!idx)
 182                return -1;
 183
 184
 185        /* create .orc_unwind_ip and .rela.orc_unwind_ip sections */
 186        sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), idx);
 187        if (!sec)
 188                return -1;
 189
 190        ip_relocsec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
 191        if (!ip_relocsec)
 192                return -1;
 193
 194        /* create .orc_unwind section */
 195        u_sec = elf_create_section(file->elf, ".orc_unwind", 0,
 196                                   sizeof(struct orc_entry), idx);
 197
 198        /* populate sections */
 199        idx = 0;
 200        for_each_sec(file, sec) {
 201                if (!sec->text)
 202                        continue;
 203
 204                prev_insn = NULL;
 205                sec_for_each_insn(file, sec, insn) {
 206                        if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc,
 207                                                 sizeof(struct orc_entry))) {
 208
 209                                if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx,
 210                                                     insn->sec, insn->offset,
 211                                                     &insn->orc))
 212                                        return -1;
 213
 214                                idx++;
 215                        }
 216                        prev_insn = insn;
 217                }
 218
 219                /* section terminator */
 220                if (prev_insn) {
 221                        if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx,
 222                                             prev_insn->sec,
 223                                             prev_insn->offset + prev_insn->len,
 224                                             &empty))
 225                                return -1;
 226
 227                        idx++;
 228                }
 229        }
 230
 231        if (elf_rebuild_reloc_section(file->elf, ip_relocsec))
 232                return -1;
 233
 234        return 0;
 235}
 236