linux/tools/objtool/orc_dump.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 <unistd.h>
   7#include <linux/objtool.h>
   8#include <asm/orc_types.h>
   9#include <objtool/objtool.h>
  10#include <objtool/warn.h>
  11#include <objtool/endianness.h>
  12
  13static const char *reg_name(unsigned int reg)
  14{
  15        switch (reg) {
  16        case ORC_REG_PREV_SP:
  17                return "prevsp";
  18        case ORC_REG_DX:
  19                return "dx";
  20        case ORC_REG_DI:
  21                return "di";
  22        case ORC_REG_BP:
  23                return "bp";
  24        case ORC_REG_SP:
  25                return "sp";
  26        case ORC_REG_R10:
  27                return "r10";
  28        case ORC_REG_R13:
  29                return "r13";
  30        case ORC_REG_BP_INDIRECT:
  31                return "bp(ind)";
  32        case ORC_REG_SP_INDIRECT:
  33                return "sp(ind)";
  34        default:
  35                return "?";
  36        }
  37}
  38
  39static const char *orc_type_name(unsigned int type)
  40{
  41        switch (type) {
  42        case UNWIND_HINT_TYPE_CALL:
  43                return "call";
  44        case UNWIND_HINT_TYPE_REGS:
  45                return "regs";
  46        case UNWIND_HINT_TYPE_REGS_PARTIAL:
  47                return "regs (partial)";
  48        default:
  49                return "?";
  50        }
  51}
  52
  53static void print_reg(unsigned int reg, int offset)
  54{
  55        if (reg == ORC_REG_BP_INDIRECT)
  56                printf("(bp%+d)", offset);
  57        else if (reg == ORC_REG_SP_INDIRECT)
  58                printf("(sp)%+d", offset);
  59        else if (reg == ORC_REG_UNDEFINED)
  60                printf("(und)");
  61        else
  62                printf("%s%+d", reg_name(reg), offset);
  63}
  64
  65int orc_dump(const char *_objname)
  66{
  67        int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0;
  68        struct orc_entry *orc = NULL;
  69        char *name;
  70        size_t nr_sections;
  71        Elf64_Addr orc_ip_addr = 0;
  72        size_t shstrtab_idx, strtab_idx = 0;
  73        Elf *elf;
  74        Elf_Scn *scn;
  75        GElf_Shdr sh;
  76        GElf_Rela rela;
  77        GElf_Sym sym;
  78        Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL;
  79
  80
  81        objname = _objname;
  82
  83        elf_version(EV_CURRENT);
  84
  85        fd = open(objname, O_RDONLY);
  86        if (fd == -1) {
  87                perror("open");
  88                return -1;
  89        }
  90
  91        elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
  92        if (!elf) {
  93                WARN_ELF("elf_begin");
  94                return -1;
  95        }
  96
  97        if (elf_getshdrnum(elf, &nr_sections)) {
  98                WARN_ELF("elf_getshdrnum");
  99                return -1;
 100        }
 101
 102        if (elf_getshdrstrndx(elf, &shstrtab_idx)) {
 103                WARN_ELF("elf_getshdrstrndx");
 104                return -1;
 105        }
 106
 107        for (i = 0; i < nr_sections; i++) {
 108                scn = elf_getscn(elf, i);
 109                if (!scn) {
 110                        WARN_ELF("elf_getscn");
 111                        return -1;
 112                }
 113
 114                if (!gelf_getshdr(scn, &sh)) {
 115                        WARN_ELF("gelf_getshdr");
 116                        return -1;
 117                }
 118
 119                name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
 120                if (!name) {
 121                        WARN_ELF("elf_strptr");
 122                        return -1;
 123                }
 124
 125                data = elf_getdata(scn, NULL);
 126                if (!data) {
 127                        WARN_ELF("elf_getdata");
 128                        return -1;
 129                }
 130
 131                if (!strcmp(name, ".symtab")) {
 132                        symtab = data;
 133                } else if (!strcmp(name, ".strtab")) {
 134                        strtab_idx = i;
 135                } else if (!strcmp(name, ".orc_unwind")) {
 136                        orc = data->d_buf;
 137                        orc_size = sh.sh_size;
 138                } else if (!strcmp(name, ".orc_unwind_ip")) {
 139                        orc_ip = data->d_buf;
 140                        orc_ip_addr = sh.sh_addr;
 141                } else if (!strcmp(name, ".rela.orc_unwind_ip")) {
 142                        rela_orc_ip = data;
 143                }
 144        }
 145
 146        if (!symtab || !strtab_idx || !orc || !orc_ip)
 147                return 0;
 148
 149        if (orc_size % sizeof(*orc) != 0) {
 150                WARN("bad .orc_unwind section size");
 151                return -1;
 152        }
 153
 154        nr_entries = orc_size / sizeof(*orc);
 155        for (i = 0; i < nr_entries; i++) {
 156                if (rela_orc_ip) {
 157                        if (!gelf_getrela(rela_orc_ip, i, &rela)) {
 158                                WARN_ELF("gelf_getrela");
 159                                return -1;
 160                        }
 161
 162                        if (!gelf_getsym(symtab, GELF_R_SYM(rela.r_info), &sym)) {
 163                                WARN_ELF("gelf_getsym");
 164                                return -1;
 165                        }
 166
 167                        if (GELF_ST_TYPE(sym.st_info) == STT_SECTION) {
 168                                scn = elf_getscn(elf, sym.st_shndx);
 169                                if (!scn) {
 170                                        WARN_ELF("elf_getscn");
 171                                        return -1;
 172                                }
 173
 174                                if (!gelf_getshdr(scn, &sh)) {
 175                                        WARN_ELF("gelf_getshdr");
 176                                        return -1;
 177                                }
 178
 179                                name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
 180                                if (!name) {
 181                                        WARN_ELF("elf_strptr");
 182                                        return -1;
 183                                }
 184                        } else {
 185                                name = elf_strptr(elf, strtab_idx, sym.st_name);
 186                                if (!name) {
 187                                        WARN_ELF("elf_strptr");
 188                                        return -1;
 189                                }
 190                        }
 191
 192                        printf("%s+%llx:", name, (unsigned long long)rela.r_addend);
 193
 194                } else {
 195                        printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i]));
 196                }
 197
 198
 199                printf(" sp:");
 200
 201                print_reg(orc[i].sp_reg, bswap_if_needed(orc[i].sp_offset));
 202
 203                printf(" bp:");
 204
 205                print_reg(orc[i].bp_reg, bswap_if_needed(orc[i].bp_offset));
 206
 207                printf(" type:%s end:%d\n",
 208                       orc_type_name(orc[i].type), orc[i].end);
 209        }
 210
 211        elf_end(elf);
 212        close(fd);
 213
 214        return 0;
 215}
 216