| 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/objtool.h> |
| 9 | #include <objtool/orc.h> |
| 10 | #include <objtool/warn.h> |
| 11 | |
| 12 | int orc_dump(const char *filename) |
| 13 | { |
| 14 | int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0; |
| 15 | struct orc_entry *orc = NULL; |
| 16 | char *name; |
| 17 | size_t nr_sections; |
| 18 | Elf64_Addr orc_ip_addr = 0; |
| 19 | size_t shstrtab_idx, strtab_idx = 0; |
| 20 | Elf *elf; |
| 21 | Elf_Scn *scn; |
| 22 | GElf_Shdr sh; |
| 23 | GElf_Rela rela; |
| 24 | GElf_Sym sym; |
| 25 | Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL; |
| 26 | struct elf dummy_elf = {}; |
| 27 | |
| 28 | elf_version(EV_CURRENT); |
| 29 | |
| 30 | fd = open(file: filename, O_RDONLY); |
| 31 | if (fd == -1) { |
| 32 | perror(s: "open" ); |
| 33 | return -1; |
| 34 | } |
| 35 | |
| 36 | elf = elf_begin(fildes: fd, ELF_C_READ_MMAP, NULL); |
| 37 | if (!elf) { |
| 38 | ERROR_ELF("elf_begin" ); |
| 39 | return -1; |
| 40 | } |
| 41 | |
| 42 | if (!elf64_getehdr(elf: elf)) { |
| 43 | ERROR_ELF("elf64_getehdr" ); |
| 44 | return -1; |
| 45 | } |
| 46 | memcpy(dest: &dummy_elf.ehdr, src: elf64_getehdr(elf: elf), n: sizeof(dummy_elf.ehdr)); |
| 47 | |
| 48 | if (elf_getshdrnum(elf: elf, dst: &nr_sections)) { |
| 49 | ERROR_ELF("elf_getshdrnum" ); |
| 50 | return -1; |
| 51 | } |
| 52 | |
| 53 | if (elf_getshdrstrndx(elf: elf, dst: &shstrtab_idx)) { |
| 54 | ERROR_ELF("elf_getshdrstrndx" ); |
| 55 | return -1; |
| 56 | } |
| 57 | |
| 58 | for (i = 0; i < nr_sections; i++) { |
| 59 | scn = elf_getscn(elf: elf, index: i); |
| 60 | if (!scn) { |
| 61 | ERROR_ELF("elf_getscn" ); |
| 62 | return -1; |
| 63 | } |
| 64 | |
| 65 | if (!gelf_getshdr(scn: scn, dst: &sh)) { |
| 66 | ERROR_ELF("gelf_getshdr" ); |
| 67 | return -1; |
| 68 | } |
| 69 | |
| 70 | name = elf_strptr(elf: elf, index: shstrtab_idx, offset: sh.sh_name); |
| 71 | if (!name) { |
| 72 | ERROR_ELF("elf_strptr" ); |
| 73 | return -1; |
| 74 | } |
| 75 | |
| 76 | data = elf_getdata(scn: scn, NULL); |
| 77 | if (!data) { |
| 78 | ERROR_ELF("elf_getdata" ); |
| 79 | return -1; |
| 80 | } |
| 81 | |
| 82 | if (!strcmp(s1: name, s2: ".symtab" )) { |
| 83 | symtab = data; |
| 84 | } else if (!strcmp(s1: name, s2: ".strtab" )) { |
| 85 | strtab_idx = i; |
| 86 | } else if (!strcmp(s1: name, s2: ".orc_unwind" )) { |
| 87 | orc = data->d_buf; |
| 88 | orc_size = sh.sh_size; |
| 89 | } else if (!strcmp(s1: name, s2: ".orc_unwind_ip" )) { |
| 90 | orc_ip = data->d_buf; |
| 91 | orc_ip_addr = sh.sh_addr; |
| 92 | } else if (!strcmp(s1: name, s2: ".rela.orc_unwind_ip" )) { |
| 93 | rela_orc_ip = data; |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | if (!symtab || !strtab_idx || !orc || !orc_ip) |
| 98 | return 0; |
| 99 | |
| 100 | if (orc_size % sizeof(*orc) != 0) { |
| 101 | ERROR("bad .orc_unwind section size" ); |
| 102 | return -1; |
| 103 | } |
| 104 | |
| 105 | nr_entries = orc_size / sizeof(*orc); |
| 106 | for (i = 0; i < nr_entries; i++) { |
| 107 | if (rela_orc_ip) { |
| 108 | if (!gelf_getrela(data: rela_orc_ip, ndx: i, dst: &rela)) { |
| 109 | ERROR_ELF("gelf_getrela" ); |
| 110 | return -1; |
| 111 | } |
| 112 | |
| 113 | if (!gelf_getsym(data: symtab, GELF_R_SYM(rela.r_info), dst: &sym)) { |
| 114 | ERROR_ELF("gelf_getsym" ); |
| 115 | return -1; |
| 116 | } |
| 117 | |
| 118 | if (GELF_ST_TYPE(sym.st_info) == STT_SECTION) { |
| 119 | scn = elf_getscn(elf: elf, index: sym.st_shndx); |
| 120 | if (!scn) { |
| 121 | ERROR_ELF("elf_getscn" ); |
| 122 | return -1; |
| 123 | } |
| 124 | |
| 125 | if (!gelf_getshdr(scn: scn, dst: &sh)) { |
| 126 | ERROR_ELF("gelf_getshdr" ); |
| 127 | return -1; |
| 128 | } |
| 129 | |
| 130 | name = elf_strptr(elf: elf, index: shstrtab_idx, offset: sh.sh_name); |
| 131 | if (!name) { |
| 132 | ERROR_ELF("elf_strptr" ); |
| 133 | return -1; |
| 134 | } |
| 135 | } else { |
| 136 | name = elf_strptr(elf: elf, index: strtab_idx, offset: sym.st_name); |
| 137 | if (!name) { |
| 138 | ERROR_ELF("elf_strptr" ); |
| 139 | return -1; |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | printf(format: "%s+%llx:" , name, (unsigned long long)rela.r_addend); |
| 144 | |
| 145 | } else { |
| 146 | printf(format: "%llx:" , (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i])); |
| 147 | } |
| 148 | |
| 149 | orc_print_dump(dummy_elf: &dummy_elf, orc, i); |
| 150 | } |
| 151 | |
| 152 | elf_end(elf: elf); |
| 153 | close(fd: fd); |
| 154 | |
| 155 | return 0; |
| 156 | } |
| 157 | |