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