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