xref: /linux/tools/perf/util/capstone.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
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
35 static 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 
50 static 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 
70 static 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 
90 static 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 
112 static 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 
132 static 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 
152 static 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 
201 static 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 
233 static 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 
259 ssize_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 
295 static 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 
357 struct find_file_offset_data {
358 	u64 ip;
359 	u64 offset;
360 };
361 
362 /* This will be called for each PHDR in an ELF binary */
363 static 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 
374 int 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 
484 out:
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 
493 err:
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 
510 int 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 
642 out:
643 	if (needs_cs_close)
644 		perf_cs_close(&handle);
645 	free(buf);
646 	return count < 0 ? count : 0;
647 
648 err:
649 	if (fd >= 0)
650 		close(fd);
651 	count = -1;
652 	goto out;
653 }
654