projects/linux·blob master · refs

653 lines · 15.5 KiB

1// SPDX-License-Identifier: GPL-2.0
2#include "capstone.h"
3
4#include <errno.h>
5#include <inttypes.h>
6#include <string.h>
7
8#include <dlfcn.h>
9#include <elf.h>
10#include <fcntl.h>
11#include <linux/ctype.h>
12
13#include <capstone/capstone.h>
14
15#include "addr_location.h"
16#include "annotate.h"
17#include "debug.h"
18#include "disasm.h"
19#include "dso.h"
20#include "machine.h"
21#include "map.h"
22#include "namespaces.h"
23#include "print_insn.h"
24#include "symbol.h"
25#include "thread.h"
26
27#if CS_VERSION_MAJOR < 5
28#define CS_ARCH_RISCV 15
29#define CS_MODE_RISCV32 1
30#define CS_MODE_RISCV64 2
31#define CS_MODE_RISCVC 4
32#endif
33
34#ifdef LIBCAPSTONE_DLOPEN
35static void *perf_cs_dll_handle(void)
36{
37 static bool dll_handle_init;
38 static void *dll_handle;
39
40 if (!dll_handle_init) {
41 dll_handle_init = true;
42 dll_handle = dlopen("libcapstone.so", RTLD_LAZY);
43 if (!dll_handle)
44 pr_debug("dlopen failed for libcapstone.so\n");
45 }
46 return dll_handle;
47}
48#endif
49
50static enum cs_err perf_cs_open(enum cs_arch arch, enum cs_mode mode, csh *handle)
51{
52#ifndef LIBCAPSTONE_DLOPEN
53 return cs_open(arch, mode, handle);
54#else
55 static bool fn_init;
56 static enum cs_err (*fn)(enum cs_arch arch, enum cs_mode mode, csh *handle);
57
58 if (!fn_init) {
59 fn = dlsym(perf_cs_dll_handle(), "cs_open");
60 if (!fn)
61 pr_debug("dlsym failed for cs_open\n");
62 fn_init = true;
63 }
64 if (!fn)
65 return CS_ERR_HANDLE;
66 return fn(arch, mode, handle);
67#endif
68}
69
70static enum cs_err perf_cs_option(csh handle, enum cs_opt_type type, size_t value)
71{
72#ifndef LIBCAPSTONE_DLOPEN
73 return cs_option(handle, type, value);
74#else
75 static bool fn_init;
76 static enum cs_err (*fn)(csh handle, enum cs_opt_type type, size_t value);
77
78 if (!fn_init) {
79 fn = dlsym(perf_cs_dll_handle(), "cs_option");
80 if (!fn)
81 pr_debug("dlsym failed for cs_option\n");
82 fn_init = true;
83 }
84 if (!fn)
85 return CS_ERR_HANDLE;
86 return fn(handle, type, value);
87#endif
88}
89
90static size_t perf_cs_disasm(csh handle, const uint8_t *code, size_t code_size,
91 uint64_t address, size_t count, struct cs_insn **insn)
92{
93#ifndef LIBCAPSTONE_DLOPEN
94 return cs_disasm(handle, code, code_size, address, count, insn);
95#else
96 static bool fn_init;
97 static enum cs_err (*fn)(csh handle, const uint8_t *code, size_t code_size,
98 uint64_t address, size_t count, struct cs_insn **insn);
99
100 if (!fn_init) {
101 fn = dlsym(perf_cs_dll_handle(), "cs_disasm");
102 if (!fn)
103 pr_debug("dlsym failed for cs_disasm\n");
104 fn_init = true;
105 }
106 if (!fn)
107 return CS_ERR_HANDLE;
108 return fn(handle, code, code_size, address, count, insn);
109#endif
110}
111
112static void perf_cs_free(struct cs_insn *insn, size_t count)
113{
114#ifndef LIBCAPSTONE_DLOPEN
115 cs_free(insn, count);
116#else
117 static bool fn_init;
118 static void (*fn)(struct cs_insn *insn, size_t count);
119
120 if (!fn_init) {
121 fn = dlsym(perf_cs_dll_handle(), "cs_free");
122 if (!fn)
123 pr_debug("dlsym failed for cs_free\n");
124 fn_init = true;
125 }
126 if (!fn)
127 return;
128 fn(insn, count);
129#endif
130}
131
132static enum cs_err perf_cs_close(csh *handle)
133{
134#ifndef LIBCAPSTONE_DLOPEN
135 return cs_close(handle);
136#else
137 static bool fn_init;
138 static enum cs_err (*fn)(csh *handle);
139
140 if (!fn_init) {
141 fn = dlsym(perf_cs_dll_handle(), "cs_close");
142 if (!fn)
143 pr_debug("dlsym failed for cs_close\n");
144 fn_init = true;
145 }
146 if (!fn)
147 return CS_ERR_HANDLE;
148 return fn(handle);
149#endif
150}
151
152static bool e_machine_to_capstone(uint16_t e_machine, bool is64, bool is_big_endian,
153 enum cs_arch *arch, enum cs_mode *mode)
154{
155 *mode = is_big_endian ? CS_MODE_BIG_ENDIAN : CS_MODE_LITTLE_ENDIAN;
156
157 switch (e_machine) {
158 case EM_X86_64:
159 case EM_386:
160 *arch = CS_ARCH_X86;
161 *mode |= is64 ? CS_MODE_64 : CS_MODE_32;
162 return true;
163 case EM_AARCH64:
164 *arch = CS_ARCH_ARM64;
165 *mode |= CS_MODE_ARM;
166 return true;
167 case EM_ARM:
168 *arch = CS_ARCH_ARM;
169 *mode |= CS_MODE_ARM | CS_MODE_V8;
170 return true;
171 case EM_S390:
172 *arch = CS_ARCH_SYSZ;
173 return true;
174 case EM_MIPS:
175 *arch = CS_ARCH_MIPS;
176 *mode |= is64 ? CS_MODE_MIPS64 : CS_MODE_MIPS32;
177 return true;
178 case EM_PPC:
179 *arch = CS_ARCH_PPC;
180 return true;
181 case EM_PPC64:
182 *arch = CS_ARCH_PPC;
183 *mode |= CS_MODE_64;
184 return true;
185 case EM_SPARC:
186 *arch = CS_ARCH_SPARC;
187 return true;
188 case EM_SPARCV9:
189 *arch = CS_ARCH_SPARC;
190 *mode |= CS_MODE_V9;
191 return true;
192 case EM_RISCV:
193 *arch = CS_ARCH_RISCV;
194 *mode |= (is64 ? CS_MODE_RISCV64 : CS_MODE_RISCV32) | CS_MODE_RISCVC;
195 return true;
196 default:
197 return false;
198 }
199}
200
201static int capstone_init(uint16_t e_machine, csh *cs_handle, bool is64, bool is_big_endian,
202 bool disassembler_style)
203{
204 enum cs_arch arch;
205 enum cs_mode mode;
206
207 if (!e_machine_to_capstone(e_machine, is64, is_big_endian, &arch, &mode))
208 return -1;
209
210 if (perf_cs_open(arch, mode, cs_handle) != CS_ERR_OK) {
211 pr_warning_once("cs_open failed\n");
212 return -1;
213 }
214
215 if (arch == CS_ARCH_X86) {
216 /*
217 * In case of using capstone_init while symbol__disassemble
218 * setting CS_OPT_SYNTAX_ATT depends if disassembler_style opts
219 * is set via annotation args
220 */
221 if (disassembler_style)
222 perf_cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
223 /*
224 * Resolving address operands to symbols is implemented
225 * on x86 by investigating instruction details.
226 */
227 perf_cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON);
228 }
229
230 return 0;
231}
232
233static size_t print_insn_x86(struct thread *thread, u8 cpumode, struct cs_insn *insn,
234 int print_opts, FILE *fp)
235{
236 struct addr_location al;
237 size_t printed = 0;
238
239 if (insn->detail && insn->detail->x86.op_count == 1) {
240 struct cs_x86_op *op = &insn->detail->x86.operands[0];
241
242 addr_location__init(&al);
243 if (op->type == X86_OP_IMM &&
244 thread__find_symbol(thread, cpumode, op->imm, &al)) {
245 printed += fprintf(fp, "%s ", insn[0].mnemonic);
246 printed += symbol__fprintf_symname_offs(al.sym, &al, fp);
247 if (print_opts & PRINT_INSN_IMM_HEX)
248 printed += fprintf(fp, " [%#" PRIx64 "]", op->imm);
249 addr_location__exit(&al);
250 return printed;
251 }
252 addr_location__exit(&al);
253 }
254
255 printed += fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);
256 return printed;
257}
258
259ssize_t capstone__fprintf_insn_asm(struct machine *machine, struct thread *thread, u8 cpumode,
260 bool is64bit, const uint8_t *code, size_t code_size, uint64_t ip,
261 int *lenp, int print_opts, FILE *fp)
262{
263 size_t printed;
264 struct cs_insn *insn;
265 csh cs_handle;
266 size_t count;
267 bool is_big_endian = false;
268 uint16_t e_machine = thread__e_machine_endian(thread, machine,
269 /*e_flags=*/NULL, &is_big_endian);
270 int ret;
271
272 /* TODO: Try to initiate capstone only once but need a proper place. */
273 ret = capstone_init(e_machine, &cs_handle, is64bit, is_big_endian,
274 /*disassembler_style=*/true);
275 if (ret < 0)
276 return ret;
277
278 count = perf_cs_disasm(cs_handle, code, code_size, ip, 1, &insn);
279 if (count > 0) {
280 if (e_machine == EM_X86_64 || e_machine == EM_386)
281 printed = print_insn_x86(thread, cpumode, &insn[0], print_opts, fp);
282 else
283 printed = fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);
284 if (lenp)
285 *lenp = insn->size;
286 perf_cs_free(insn, count);
287 } else {
288 printed = -1;
289 }
290
291 perf_cs_close(&cs_handle);
292 return printed;
293}
294
295static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,
296 struct annotate_args *args, u64 addr)
297{
298 int i;
299 struct map *map = args->ms->map;
300 struct symbol *sym;
301
302 /* TODO: support more architectures */
303 if (!arch__is_x86(args->arch))
304 return;
305
306 if (insn->detail == NULL)
307 return;
308
309 for (i = 0; i < insn->detail->x86.op_count; i++) {
310 struct cs_x86_op *op = &insn->detail->x86.operands[i];
311 u64 orig_addr;
312 struct map *found_map = NULL;
313
314 if (op->type != X86_OP_MEM)
315 continue;
316
317 /* only print RIP-based global symbols for now */
318 if (op->mem.base != X86_REG_RIP)
319 continue;
320
321 /* get the target address */
322 orig_addr = addr + insn->size + op->mem.disp;
323 addr = map__objdump_2mem(map, orig_addr);
324
325 if (dso__kernel(map__dso(map))) {
326 /*
327 * The kernel maps can be split into sections, let's
328 * find the map first and then search the symbol.
329 */
330 found_map = maps__find(map__kmaps(map), addr);
331 if (found_map == NULL)
332 continue;
333 map = found_map;
334 }
335
336 /* convert it to map-relative address for search */
337 addr = map__map_ip(map, addr);
338
339 sym = map__find_symbol(map, addr);
340 if (sym == NULL) {
341 map__put(found_map);
342 continue;
343 }
344
345 if (addr == sym->start) {
346 scnprintf(buf, len, "\t# %"PRIx64" <%s>",
347 orig_addr, sym->name);
348 } else {
349 scnprintf(buf, len, "\t# %"PRIx64" <%s+%#"PRIx64">",
350 orig_addr, sym->name, addr - sym->start);
351 }
352 map__put(found_map);
353 break;
354 }
355}
356
357struct find_file_offset_data {
358 u64 ip;
359 u64 offset;
360};
361
362/* This will be called for each PHDR in an ELF binary */
363static int find_file_offset(u64 start, u64 len, u64 pgoff, void *arg)
364{
365 struct find_file_offset_data *data = arg;
366
367 if (start <= data->ip && data->ip < start + len) {
368 data->offset = pgoff + data->ip - start;
369 return 1;
370 }
371 return 0;
372}
373
374int symbol__disassemble_capstone(const char *filename, struct symbol *sym,
375 struct annotate_args *args)
376{
377 struct annotation *notes = symbol__annotation(sym);
378 struct map *map = args->ms->map;
379 struct dso *dso = map__dso(map);
380 u64 start = map__rip_2objdump(map, sym->start);
381 u64 offset;
382 int i, count, free_count;
383 bool is_64bit = false;
384 bool needs_cs_close = false;
385 /* Malloc-ed buffer containing instructions read from disk. */
386 u8 *code_buf = NULL;
387 /* Pointer to code to be disassembled. */
388 const u8 *buf;
389 u64 buf_len;
390 csh handle;
391 struct cs_insn *insn = NULL;
392 char disasm_buf[512];
393 struct disasm_line *dl;
394 bool disassembler_style = false;
395 uint16_t e_machine = EM_NONE;
396 bool is_big_endian = false;
397
398 if (args->options->objdump_path)
399 return -1;
400
401 buf = dso__read_symbol(dso, filename, map, sym,
402 &code_buf, &buf_len, &is_64bit);
403 if (buf == NULL)
404 return errno;
405
406 /* add the function address and name */
407 scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
408 start, sym->name);
409
410 args->offset = -1;
411 args->line = disasm_buf;
412 args->line_nr = 0;
413 args->fileloc = NULL;
414 args->ms->sym = sym;
415
416 dl = disasm_line__new(args);
417 if (dl == NULL)
418 goto err;
419
420 annotation_line__add(&dl->al, &notes->src->source);
421
422 if (!args->options->disassembler_style ||
423 !strcmp(args->options->disassembler_style, "att"))
424 disassembler_style = true;
425
426 if (args->ms->thread) {
427 e_machine = thread__e_machine_endian(args->ms->thread,
428 /*machine=*/NULL,
429 /*e_flags=*/NULL, &is_big_endian);
430 } else if (dso) {
431 struct maps *kmaps = (map && dso__kernel(dso)) ? map__kmaps(map) : NULL;
432 struct machine *kmap_machine = kmaps ? maps__machine(kmaps) : NULL;
433
434 e_machine = dso__e_machine_endian(dso, kmap_machine, /*e_flags=*/NULL,
435 &is_big_endian);
436 }
437 if (!e_machine || e_machine == EM_NONE) {
438 e_machine = thread__e_machine_endian(NULL,
439 /*machine=*/NULL,
440 /*e_flags=*/NULL, &is_big_endian);
441 }
442 if (capstone_init(e_machine, &handle, is_64bit, is_big_endian, disassembler_style) < 0)
443 goto err;
444
445 needs_cs_close = true;
446
447 free_count = count = perf_cs_disasm(handle, buf, buf_len, start, buf_len, &insn);
448 for (i = 0, offset = 0; i < count; i++) {
449 int printed;
450
451 printed = scnprintf(disasm_buf, sizeof(disasm_buf),
452 " %-7s %s",
453 insn[i].mnemonic, insn[i].op_str);
454 print_capstone_detail(&insn[i], disasm_buf + printed,
455 sizeof(disasm_buf) - printed, args,
456 start + offset);
457
458 args->offset = offset;
459 args->line = disasm_buf;
460
461 dl = disasm_line__new(args);
462 if (dl == NULL)
463 goto err;
464
465 annotation_line__add(&dl->al, &notes->src->source);
466
467 offset += insn[i].size;
468 }
469
470 /* It failed in the middle: probably due to unknown instructions */
471 if (offset != buf_len) {
472 struct list_head *list = &notes->src->source;
473
474 /* Discard all lines and fallback to objdump */
475 while (!list_empty(list)) {
476 dl = list_first_entry(list, struct disasm_line, al.node);
477
478 list_del_init(&dl->al.node);
479 disasm_line__free(dl);
480 }
481 count = -1;
482 }
483
484out:
485 if (needs_cs_close) {
486 perf_cs_close(&handle);
487 if (free_count > 0)
488 perf_cs_free(insn, free_count);
489 }
490 free(code_buf);
491 return count < 0 ? count : 0;
492
493err:
494 if (needs_cs_close) {
495 struct disasm_line *tmp;
496
497 /*
498 * It probably failed in the middle of the above loop.
499 * Release any resources it might add.
500 */
501 list_for_each_entry_safe(dl, tmp, &notes->src->source, al.node) {
502 list_del(&dl->al.node);
503 disasm_line__free(dl);
504 }
505 }
506 count = -1;
507 goto out;
508}
509
510int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
511 struct symbol *sym __maybe_unused,
512 struct annotate_args *args __maybe_unused)
513{
514 struct annotation *notes = symbol__annotation(sym);
515 struct map *map = args->ms->map;
516 struct dso *dso = map__dso(map);
517 struct nscookie nsc;
518 u64 start = map__rip_2objdump(map, sym->start);
519 u64 end = map__rip_2objdump(map, sym->end);
520 u64 len = end - start;
521 u64 offset;
522 int i, fd, count;
523 bool is_64bit = false;
524 bool needs_cs_close = false;
525 u8 *buf = NULL;
526 struct find_file_offset_data data = {
527 .ip = start,
528 };
529 csh handle;
530 char disasm_buf[512];
531 struct disasm_line *dl;
532 u32 *line;
533 bool disassembler_style = false;
534 uint16_t e_machine = EM_NONE;
535 bool is_big_endian = false;
536
537 if (args->options->objdump_path)
538 return -1;
539
540 nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
541 fd = open(filename, O_RDONLY);
542 nsinfo__mountns_exit(&nsc);
543 if (fd < 0)
544 return -1;
545
546 if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data,
547 &is_64bit) == 0)
548 goto err;
549
550 if (!args->options->disassembler_style ||
551 !strcmp(args->options->disassembler_style, "att"))
552 disassembler_style = true;
553
554 if (args->ms->thread) {
555 e_machine = thread__e_machine_endian(args->ms->thread,
556 /*machine=*/NULL,
557 /*e_flags=*/NULL, &is_big_endian);
558 } else if (dso) {
559 struct maps *kmaps = (map && dso__kernel(dso)) ? map__kmaps(map) : NULL;
560 struct machine *kmap_machine = kmaps ? maps__machine(kmaps) : NULL;
561
562 e_machine = dso__e_machine_endian(dso, kmap_machine, /*e_flags=*/NULL,
563 &is_big_endian);
564 }
565 if (!e_machine || e_machine == EM_NONE) {
566 e_machine = thread__e_machine_endian(NULL,
567 /*machine=*/NULL,
568 /*e_flags=*/NULL, &is_big_endian);
569 }
570 if (capstone_init(e_machine, &handle, is_64bit, is_big_endian, disassembler_style) < 0)
571 goto err;
572
573 needs_cs_close = true;
574
575 buf = malloc(len);
576 if (buf == NULL)
577 goto err;
578
579 count = pread(fd, buf, len, data.offset);
580 close(fd);
581 fd = -1;
582
583 if ((u64)count != len)
584 goto err;
585
586 line = (u32 *)buf;
587
588 /* add the function address and name */
589 scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
590 start, sym->name);
591
592 args->offset = -1;
593 args->line = disasm_buf;
594 args->line_nr = 0;
595 args->fileloc = NULL;
596 args->ms->sym = sym;
597
598 dl = disasm_line__new(args);
599 if (dl == NULL)
600 goto err;
601
602 annotation_line__add(&dl->al, &notes->src->source);
603
604 /*
605 * TODO: enable disassm for powerpc
606 * count = cs_disasm(handle, buf, len, start, len, &insn);
607 *
608 * For now, only binary code is saved in disassembled line
609 * to be used in "type" and "typeoff" sort keys. Each raw code
610 * is 32 bit instruction. So use "len/4" to get the number of
611 * entries.
612 */
613 count = len/4;
614
615 for (i = 0, offset = 0; i < count; i++) {
616 args->offset = offset;
617 sprintf(args->line, "%x", line[i]);
618
619 dl = disasm_line__new(args);
620 if (dl == NULL)
621 break;
622
623 annotation_line__add(&dl->al, &notes->src->source);
624
625 offset += 4;
626 }
627
628 /* It failed in the middle */
629 if (offset != len) {
630 struct list_head *list = &notes->src->source;
631
632 /* Discard all lines and fallback to objdump */
633 while (!list_empty(list)) {
634 dl = list_first_entry(list, struct disasm_line, al.node);
635
636 list_del_init(&dl->al.node);
637 disasm_line__free(dl);
638 }
639 count = -1;
640 }
641
642out:
643 if (needs_cs_close)
644 perf_cs_close(&handle);
645 free(buf);
646 return count < 0 ? count : 0;
647
648err:
649 if (fd >= 0)
650 close(fd);
651 count = -1;
652 goto out;
653}