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