653 lines · 15.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include "capstone.h" #include <errno.h>#include <inttypes.h>#include <string.h> #include <dlfcn.h>#include <elf.h>#include <fcntl.h>#include <linux/ctype.h> #include <capstone/capstone.h> #include "addr_location.h"#include "annotate.h"#include "debug.h"#include "disasm.h"#include "dso.h"#include "machine.h"#include "map.h"#include "namespaces.h"#include "print_insn.h"#include "symbol.h"#include "thread.h" #if CS_VERSION_MAJOR < 5#define CS_ARCH_RISCV    15#define CS_MODE_RISCV32  1#define CS_MODE_RISCV64  2#define CS_MODE_RISCVC   4#endif #ifdef LIBCAPSTONE_DLOPENstatic void *perf_cs_dll_handle(void){	static bool dll_handle_init;	static void *dll_handle; 	if (!dll_handle_init) {		dll_handle_init = true;		dll_handle = dlopen("libcapstone.so", RTLD_LAZY);		if (!dll_handle)			pr_debug("dlopen failed for libcapstone.so\n");	}	return dll_handle;}#endif static enum cs_err perf_cs_open(enum cs_arch arch, enum cs_mode mode, csh *handle){#ifndef LIBCAPSTONE_DLOPEN	return cs_open(arch, mode, handle);#else	static bool fn_init;	static enum cs_err (*fn)(enum cs_arch arch, enum cs_mode mode, csh *handle); 	if (!fn_init) {		fn = dlsym(perf_cs_dll_handle(), "cs_open");		if (!fn)			pr_debug("dlsym failed for cs_open\n");		fn_init = true;	}	if (!fn)		return CS_ERR_HANDLE;	return fn(arch, mode, handle);#endif} static enum cs_err perf_cs_option(csh handle, enum cs_opt_type type, size_t value){#ifndef LIBCAPSTONE_DLOPEN	return cs_option(handle, type, value);#else	static bool fn_init;	static enum cs_err (*fn)(csh handle, enum cs_opt_type type, size_t value); 	if (!fn_init) {		fn = dlsym(perf_cs_dll_handle(), "cs_option");		if (!fn)			pr_debug("dlsym failed for cs_option\n");		fn_init = true;	}	if (!fn)		return CS_ERR_HANDLE;	return fn(handle, type, value);#endif} static size_t perf_cs_disasm(csh handle, const uint8_t *code, size_t code_size,			uint64_t address, size_t count, struct cs_insn **insn){#ifndef LIBCAPSTONE_DLOPEN	return cs_disasm(handle, code, code_size, address, count, insn);#else	static bool fn_init;	static enum cs_err (*fn)(csh handle, const uint8_t *code, size_t code_size,				 uint64_t address, size_t count, struct cs_insn **insn); 	if (!fn_init) {		fn = dlsym(perf_cs_dll_handle(), "cs_disasm");		if (!fn)			pr_debug("dlsym failed for cs_disasm\n");		fn_init = true;	}	if (!fn)		return CS_ERR_HANDLE;	return fn(handle, code, code_size, address, count, insn);#endif} static void perf_cs_free(struct cs_insn *insn, size_t count){#ifndef LIBCAPSTONE_DLOPEN	cs_free(insn, count);#else	static bool fn_init;	static void (*fn)(struct cs_insn *insn, size_t count); 	if (!fn_init) {		fn = dlsym(perf_cs_dll_handle(), "cs_free");		if (!fn)			pr_debug("dlsym failed for cs_free\n");		fn_init = true;	}	if (!fn)		return;	fn(insn, count);#endif} static enum cs_err perf_cs_close(csh *handle){#ifndef LIBCAPSTONE_DLOPEN	return cs_close(handle);#else	static bool fn_init;	static enum cs_err (*fn)(csh *handle); 	if (!fn_init) {		fn = dlsym(perf_cs_dll_handle(), "cs_close");		if (!fn)			pr_debug("dlsym failed for cs_close\n");		fn_init = true;	}	if (!fn)		return CS_ERR_HANDLE;	return fn(handle);#endif} static bool e_machine_to_capstone(uint16_t e_machine, bool is64, bool is_big_endian,				  enum cs_arch *arch, enum cs_mode *mode){	*mode = is_big_endian ? CS_MODE_BIG_ENDIAN : CS_MODE_LITTLE_ENDIAN; 	switch (e_machine) {	case EM_X86_64:	case EM_386:		*arch = CS_ARCH_X86;		*mode |= is64 ? CS_MODE_64 : CS_MODE_32;		return true;	case EM_AARCH64:		*arch = CS_ARCH_ARM64;		*mode |= CS_MODE_ARM;		return true;	case EM_ARM:		*arch = CS_ARCH_ARM;		*mode |= CS_MODE_ARM | CS_MODE_V8;		return true;	case EM_S390:		*arch = CS_ARCH_SYSZ;		return true;	case EM_MIPS:		*arch = CS_ARCH_MIPS;		*mode |= is64 ? CS_MODE_MIPS64 : CS_MODE_MIPS32;		return true;	case EM_PPC:		*arch = CS_ARCH_PPC;		return true;	case EM_PPC64:		*arch = CS_ARCH_PPC;		*mode |= CS_MODE_64;		return true;	case EM_SPARC:		*arch = CS_ARCH_SPARC;		return true;	case EM_SPARCV9:		*arch = CS_ARCH_SPARC;		*mode |= CS_MODE_V9;		return true;	case EM_RISCV:		*arch = CS_ARCH_RISCV;		*mode |= (is64 ? CS_MODE_RISCV64 : CS_MODE_RISCV32) | CS_MODE_RISCVC;		return true;	default:		return false;	}} static int capstone_init(uint16_t e_machine, csh *cs_handle, bool is64, bool is_big_endian,			 bool disassembler_style){	enum cs_arch arch;	enum cs_mode mode; 	if (!e_machine_to_capstone(e_machine, is64, is_big_endian, &arch, &mode))		return -1; 	if (perf_cs_open(arch, mode, cs_handle) != CS_ERR_OK) {		pr_warning_once("cs_open failed\n");		return -1;	} 	if (arch == CS_ARCH_X86) {		/*		 * In case of using capstone_init while symbol__disassemble		 * setting CS_OPT_SYNTAX_ATT depends if disassembler_style opts		 * is set via annotation args		 */		if (disassembler_style)			perf_cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);		/*		 * Resolving address operands to symbols is implemented		 * on x86 by investigating instruction details.		 */		perf_cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON);	} 	return 0;} static size_t print_insn_x86(struct thread *thread, u8 cpumode, struct cs_insn *insn,			     int print_opts, FILE *fp){	struct addr_location al;	size_t printed = 0; 	if (insn->detail && insn->detail->x86.op_count == 1) {		struct cs_x86_op *op = &insn->detail->x86.operands[0]; 		addr_location__init(&al);		if (op->type == X86_OP_IMM &&		    thread__find_symbol(thread, cpumode, op->imm, &al)) {			printed += fprintf(fp, "%s ", insn[0].mnemonic);			printed += symbol__fprintf_symname_offs(al.sym, &al, fp);			if (print_opts & PRINT_INSN_IMM_HEX)				printed += fprintf(fp, " [%#" PRIx64 "]", op->imm);			addr_location__exit(&al);			return printed;		}		addr_location__exit(&al);	} 	printed += fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);	return printed;} ssize_t capstone__fprintf_insn_asm(struct machine *machine, struct thread *thread, u8 cpumode,				   bool is64bit, const uint8_t *code, size_t code_size, uint64_t ip,				   int *lenp, int print_opts, FILE *fp){	size_t printed;	struct cs_insn *insn;	csh cs_handle;	size_t count;	bool is_big_endian = false;	uint16_t e_machine = thread__e_machine_endian(thread, machine,						      /*e_flags=*/NULL, &is_big_endian);	int ret; 	/* TODO: Try to initiate capstone only once but need a proper place. */	ret = capstone_init(e_machine, &cs_handle, is64bit, is_big_endian,			    /*disassembler_style=*/true);	if (ret < 0)		return ret; 	count = perf_cs_disasm(cs_handle, code, code_size, ip, 1, &insn);	if (count > 0) {		if (e_machine == EM_X86_64 || e_machine == EM_386)			printed = print_insn_x86(thread, cpumode, &insn[0], print_opts, fp);		else			printed = fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);		if (lenp)			*lenp = insn->size;		perf_cs_free(insn, count);	} else {		printed = -1;	} 	perf_cs_close(&cs_handle);	return printed;} static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,				  struct annotate_args *args, u64 addr){	int i;	struct map *map = args->ms->map;	struct symbol *sym; 	/* TODO: support more architectures */	if (!arch__is_x86(args->arch))		return; 	if (insn->detail == NULL)		return; 	for (i = 0; i < insn->detail->x86.op_count; i++) {		struct cs_x86_op *op = &insn->detail->x86.operands[i];		u64 orig_addr;		struct map *found_map = NULL; 		if (op->type != X86_OP_MEM)			continue; 		/* only print RIP-based global symbols for now */		if (op->mem.base != X86_REG_RIP)			continue; 		/* get the target address */		orig_addr = addr + insn->size + op->mem.disp;		addr = map__objdump_2mem(map, orig_addr); 		if (dso__kernel(map__dso(map))) {			/*			 * The kernel maps can be split into sections, let's			 * find the map first and then search the symbol.			 */			found_map = maps__find(map__kmaps(map), addr);			if (found_map == NULL)				continue;			map = found_map;		} 		/* convert it to map-relative address for search */		addr = map__map_ip(map, addr); 		sym = map__find_symbol(map, addr);		if (sym == NULL) {			map__put(found_map);			continue;		} 		if (addr == sym->start) {			scnprintf(buf, len, "\t# %"PRIx64" <%s>",				  orig_addr, sym->name);		} else {			scnprintf(buf, len, "\t# %"PRIx64" <%s+%#"PRIx64">",				  orig_addr, sym->name, addr - sym->start);		}		map__put(found_map);		break;	}} struct find_file_offset_data {	u64 ip;	u64 offset;}; /* This will be called for each PHDR in an ELF binary */static int find_file_offset(u64 start, u64 len, u64 pgoff, void *arg){	struct find_file_offset_data *data = arg; 	if (start <= data->ip && data->ip < start + len) {		data->offset = pgoff + data->ip - start;		return 1;	}	return 0;} int symbol__disassemble_capstone(const char *filename, struct symbol *sym,				 struct annotate_args *args){	struct annotation *notes = symbol__annotation(sym);	struct map *map = args->ms->map;	struct dso *dso = map__dso(map);	u64 start = map__rip_2objdump(map, sym->start);	u64 offset;	int i, count, free_count;	bool is_64bit = false;	bool needs_cs_close = false;	/* Malloc-ed buffer containing instructions read from disk. */	u8 *code_buf = NULL;	/* Pointer to code to be disassembled. */	const u8 *buf;	u64 buf_len;	csh handle;	struct cs_insn *insn = NULL;	char disasm_buf[512];	struct disasm_line *dl;	bool disassembler_style = false;	uint16_t e_machine = EM_NONE;	bool is_big_endian = false; 	if (args->options->objdump_path)		return -1; 	buf = dso__read_symbol(dso, filename, map, sym,			       &code_buf, &buf_len, &is_64bit);	if (buf == NULL)		return errno; 	/* add the function address and name */	scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",		  start, sym->name); 	args->offset = -1;	args->line = disasm_buf;	args->line_nr = 0;	args->fileloc = NULL;	args->ms->sym = sym; 	dl = disasm_line__new(args);	if (dl == NULL)		goto err; 	annotation_line__add(&dl->al, &notes->src->source); 	if (!args->options->disassembler_style ||	    !strcmp(args->options->disassembler_style, "att"))		disassembler_style = true; 	if (args->ms->thread) {		e_machine = thread__e_machine_endian(args->ms->thread,						     /*machine=*/NULL,						     /*e_flags=*/NULL, &is_big_endian);	} else if (dso) {		struct maps *kmaps = (map && dso__kernel(dso)) ? map__kmaps(map) : NULL;		struct machine *kmap_machine = kmaps ? maps__machine(kmaps) : NULL; 		e_machine = dso__e_machine_endian(dso, kmap_machine, /*e_flags=*/NULL,						  &is_big_endian);	}	if (!e_machine || e_machine == EM_NONE) {		e_machine = thread__e_machine_endian(NULL,						     /*machine=*/NULL,						     /*e_flags=*/NULL, &is_big_endian);	}	if (capstone_init(e_machine, &handle, is_64bit, is_big_endian, disassembler_style) < 0)		goto err; 	needs_cs_close = true; 	free_count = count = perf_cs_disasm(handle, buf, buf_len, start, buf_len, &insn);	for (i = 0, offset = 0; i < count; i++) {		int printed; 		printed = scnprintf(disasm_buf, sizeof(disasm_buf),				    "       %-7s %s",				    insn[i].mnemonic, insn[i].op_str);		print_capstone_detail(&insn[i], disasm_buf + printed,				      sizeof(disasm_buf) - printed, args,				      start + offset); 		args->offset = offset;		args->line = disasm_buf; 		dl = disasm_line__new(args);		if (dl == NULL)			goto err; 		annotation_line__add(&dl->al, &notes->src->source); 		offset += insn[i].size;	} 	/* It failed in the middle: probably due to unknown instructions */	if (offset != buf_len) {		struct list_head *list = &notes->src->source; 		/* Discard all lines and fallback to objdump */		while (!list_empty(list)) {			dl = list_first_entry(list, struct disasm_line, al.node); 			list_del_init(&dl->al.node);			disasm_line__free(dl);		}		count = -1;	} out:	if (needs_cs_close) {		perf_cs_close(&handle);		if (free_count > 0)			perf_cs_free(insn, free_count);	}	free(code_buf);	return count < 0 ? count : 0; err:	if (needs_cs_close) {		struct disasm_line *tmp; 		/*		 * It probably failed in the middle of the above loop.		 * Release any resources it might add.		 */

Lines 1–500 of 653.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t