xref: /linux/tools/perf/util/capstone.c (revision b2d4225695e6977cd7802fe425d8caa5822a5e7d)
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;
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 	e_machine = thread__e_machine_endian(args->ms->thread,
427 					     /*machine=*/NULL,
428 					     /*e_flags=*/NULL, &is_big_endian);
429 	if (capstone_init(e_machine, &handle, is_64bit, is_big_endian, disassembler_style) < 0)
430 		goto err;
431 
432 	needs_cs_close = true;
433 
434 	free_count = count = perf_cs_disasm(handle, buf, buf_len, start, buf_len, &insn);
435 	for (i = 0, offset = 0; i < count; i++) {
436 		int printed;
437 
438 		printed = scnprintf(disasm_buf, sizeof(disasm_buf),
439 				    "       %-7s %s",
440 				    insn[i].mnemonic, insn[i].op_str);
441 		print_capstone_detail(&insn[i], disasm_buf + printed,
442 				      sizeof(disasm_buf) - printed, args,
443 				      start + offset);
444 
445 		args->offset = offset;
446 		args->line = disasm_buf;
447 
448 		dl = disasm_line__new(args);
449 		if (dl == NULL)
450 			goto err;
451 
452 		annotation_line__add(&dl->al, &notes->src->source);
453 
454 		offset += insn[i].size;
455 	}
456 
457 	/* It failed in the middle: probably due to unknown instructions */
458 	if (offset != buf_len) {
459 		struct list_head *list = &notes->src->source;
460 
461 		/* Discard all lines and fallback to objdump */
462 		while (!list_empty(list)) {
463 			dl = list_first_entry(list, struct disasm_line, al.node);
464 
465 			list_del_init(&dl->al.node);
466 			disasm_line__free(dl);
467 		}
468 		count = -1;
469 	}
470 
471 out:
472 	if (needs_cs_close) {
473 		perf_cs_close(&handle);
474 		if (free_count > 0)
475 			perf_cs_free(insn, free_count);
476 	}
477 	free(code_buf);
478 	return count < 0 ? count : 0;
479 
480 err:
481 	if (needs_cs_close) {
482 		struct disasm_line *tmp;
483 
484 		/*
485 		 * It probably failed in the middle of the above loop.
486 		 * Release any resources it might add.
487 		 */
488 		list_for_each_entry_safe(dl, tmp, &notes->src->source, al.node) {
489 			list_del(&dl->al.node);
490 			disasm_line__free(dl);
491 		}
492 	}
493 	count = -1;
494 	goto out;
495 }
496 
497 int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
498 					 struct symbol *sym __maybe_unused,
499 					 struct annotate_args *args __maybe_unused)
500 {
501 	struct annotation *notes = symbol__annotation(sym);
502 	struct map *map = args->ms->map;
503 	struct dso *dso = map__dso(map);
504 	struct nscookie nsc;
505 	u64 start = map__rip_2objdump(map, sym->start);
506 	u64 end = map__rip_2objdump(map, sym->end);
507 	u64 len = end - start;
508 	u64 offset;
509 	int i, fd, count;
510 	bool is_64bit = false;
511 	bool needs_cs_close = false;
512 	u8 *buf = NULL;
513 	struct find_file_offset_data data = {
514 		.ip = start,
515 	};
516 	csh handle;
517 	char disasm_buf[512];
518 	struct disasm_line *dl;
519 	u32 *line;
520 	bool disassembler_style = false;
521 	uint16_t e_machine;
522 	bool is_big_endian = false;
523 
524 	if (args->options->objdump_path)
525 		return -1;
526 
527 	nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
528 	fd = open(filename, O_RDONLY);
529 	nsinfo__mountns_exit(&nsc);
530 	if (fd < 0)
531 		return -1;
532 
533 	if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data,
534 			    &is_64bit) == 0)
535 		goto err;
536 
537 	if (!args->options->disassembler_style ||
538 	    !strcmp(args->options->disassembler_style, "att"))
539 		disassembler_style = true;
540 
541 	e_machine = thread__e_machine_endian(args->ms->thread,
542 					     /*machine=*/NULL,
543 					     /*e_flags=*/NULL, &is_big_endian);
544 	if (capstone_init(e_machine, &handle, is_64bit, is_big_endian, disassembler_style) < 0)
545 		goto err;
546 
547 	needs_cs_close = true;
548 
549 	buf = malloc(len);
550 	if (buf == NULL)
551 		goto err;
552 
553 	count = pread(fd, buf, len, data.offset);
554 	close(fd);
555 	fd = -1;
556 
557 	if ((u64)count != len)
558 		goto err;
559 
560 	line = (u32 *)buf;
561 
562 	/* add the function address and name */
563 	scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
564 		  start, sym->name);
565 
566 	args->offset = -1;
567 	args->line = disasm_buf;
568 	args->line_nr = 0;
569 	args->fileloc = NULL;
570 	args->ms->sym = sym;
571 
572 	dl = disasm_line__new(args);
573 	if (dl == NULL)
574 		goto err;
575 
576 	annotation_line__add(&dl->al, &notes->src->source);
577 
578 	/*
579 	 * TODO: enable disassm for powerpc
580 	 * count = cs_disasm(handle, buf, len, start, len, &insn);
581 	 *
582 	 * For now, only binary code is saved in disassembled line
583 	 * to be used in "type" and "typeoff" sort keys. Each raw code
584 	 * is 32 bit instruction. So use "len/4" to get the number of
585 	 * entries.
586 	 */
587 	count = len/4;
588 
589 	for (i = 0, offset = 0; i < count; i++) {
590 		args->offset = offset;
591 		sprintf(args->line, "%x", line[i]);
592 
593 		dl = disasm_line__new(args);
594 		if (dl == NULL)
595 			break;
596 
597 		annotation_line__add(&dl->al, &notes->src->source);
598 
599 		offset += 4;
600 	}
601 
602 	/* It failed in the middle */
603 	if (offset != len) {
604 		struct list_head *list = &notes->src->source;
605 
606 		/* Discard all lines and fallback to objdump */
607 		while (!list_empty(list)) {
608 			dl = list_first_entry(list, struct disasm_line, al.node);
609 
610 			list_del_init(&dl->al.node);
611 			disasm_line__free(dl);
612 		}
613 		count = -1;
614 	}
615 
616 out:
617 	if (needs_cs_close)
618 		perf_cs_close(&handle);
619 	free(buf);
620 	return count < 0 ? count : 0;
621 
622 err:
623 	if (fd >= 0)
624 		close(fd);
625 	count = -1;
626 	goto out;
627 }
628