brintos

brintos / linux-shallow public Read only

0
0
Text · 8.2 KiB · 7b8d9ec Raw
404 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)2/*3 * Based on:4 *5 * Minimal BPF JIT image disassembler6 *7 * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for8 * debugging or verification purposes.9 *10 * Copyright 2013 Daniel Borkmann <daniel@iogearbox.net>11 * Licensed under the GNU General Public License, version 2.0 (GPLv2)12 */13 14#ifndef _GNU_SOURCE15#define _GNU_SOURCE16#endif17#include <stdio.h>18#include <stdarg.h>19#include <stdint.h>20#include <stdlib.h>21#include <unistd.h>22#include <string.h>23#include <sys/stat.h>24#include <limits.h>25#include <bpf/libbpf.h>26 27#ifdef HAVE_LLVM_SUPPORT28#include <llvm-c/Core.h>29#include <llvm-c/Disassembler.h>30#include <llvm-c/Target.h>31#include <llvm-c/TargetMachine.h>32#endif33 34#ifdef HAVE_LIBBFD_SUPPORT35#include <bfd.h>36#include <dis-asm.h>37#include <tools/dis-asm-compat.h>38#endif39 40#include "json_writer.h"41#include "main.h"42 43static int oper_count;44 45#ifdef HAVE_LLVM_SUPPORT46#define DISASM_SPACER47 48typedef LLVMDisasmContextRef disasm_ctx_t;49 50static int printf_json(char *s)51{52	s = strtok(s, " \t");53	jsonw_string_field(json_wtr, "operation", s);54 55	jsonw_name(json_wtr, "operands");56	jsonw_start_array(json_wtr);57	oper_count = 1;58 59	while ((s = strtok(NULL, " \t,()")) != 0) {60		jsonw_string(json_wtr, s);61		oper_count++;62	}63	return 0;64}65 66/* This callback to set the ref_type is necessary to have the LLVM disassembler67 * print PC-relative addresses instead of byte offsets for branch instruction68 * targets.69 */70static const char *71symbol_lookup_callback(__maybe_unused void *disasm_info,72		       __maybe_unused uint64_t ref_value,73		       uint64_t *ref_type, __maybe_unused uint64_t ref_PC,74		       __maybe_unused const char **ref_name)75{76	*ref_type = LLVMDisassembler_ReferenceType_InOut_None;77	return NULL;78}79 80static int81init_context(disasm_ctx_t *ctx, const char *arch,82	     __maybe_unused const char *disassembler_options,83	     __maybe_unused unsigned char *image, __maybe_unused ssize_t len)84{85	char *triple;86 87	if (arch)88		triple = LLVMNormalizeTargetTriple(arch);89	else90		triple = LLVMGetDefaultTargetTriple();91	if (!triple) {92		p_err("Failed to retrieve triple");93		return -1;94	}95	*ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback);96	LLVMDisposeMessage(triple);97 98	if (!*ctx) {99		p_err("Failed to create disassembler");100		return -1;101	}102 103	return 0;104}105 106static void destroy_context(disasm_ctx_t *ctx)107{108	LLVMDisposeMessage(*ctx);109}110 111static int112disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc)113{114	char buf[256];115	int count;116 117	count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, pc,118				      buf, sizeof(buf));119	if (json_output)120		printf_json(buf);121	else122		printf("%s", buf);123 124	return count;125}126 127int disasm_init(void)128{129	LLVMInitializeAllTargetInfos();130	LLVMInitializeAllTargetMCs();131	LLVMInitializeAllDisassemblers();132	return 0;133}134#endif /* HAVE_LLVM_SUPPORT */135 136#ifdef HAVE_LIBBFD_SUPPORT137#define DISASM_SPACER "\t"138 139typedef struct {140	struct disassemble_info *info;141	disassembler_ftype disassemble;142	bfd *bfdf;143} disasm_ctx_t;144 145static int get_exec_path(char *tpath, size_t size)146{147	const char *path = "/proc/self/exe";148	ssize_t len;149 150	len = readlink(path, tpath, size - 1);151	if (len <= 0)152		return -1;153 154	tpath[len] = 0;155 156	return 0;157}158 159static int printf_json(void *out, const char *fmt, va_list ap)160{161	char *s;162	int err;163 164	err = vasprintf(&s, fmt, ap);165	if (err < 0)166		return -1;167 168	if (!oper_count) {169		int i;170 171		/* Strip trailing spaces */172		i = strlen(s) - 1;173		while (s[i] == ' ')174			s[i--] = '\0';175 176		jsonw_string_field(json_wtr, "operation", s);177		jsonw_name(json_wtr, "operands");178		jsonw_start_array(json_wtr);179		oper_count++;180	} else if (!strcmp(fmt, ",")) {181		   /* Skip */182	} else {183		jsonw_string(json_wtr, s);184		oper_count++;185	}186	free(s);187	return 0;188}189 190static int fprintf_json(void *out, const char *fmt, ...)191{192	va_list ap;193	int r;194 195	va_start(ap, fmt);196	r = printf_json(out, fmt, ap);197	va_end(ap);198 199	return r;200}201 202static int fprintf_json_styled(void *out,203			       enum disassembler_style style __maybe_unused,204			       const char *fmt, ...)205{206	va_list ap;207	int r;208 209	va_start(ap, fmt);210	r = printf_json(out, fmt, ap);211	va_end(ap);212 213	return r;214}215 216static int init_context(disasm_ctx_t *ctx, const char *arch,217			const char *disassembler_options,218			unsigned char *image, ssize_t len)219{220	struct disassemble_info *info;221	char tpath[PATH_MAX];222	bfd *bfdf;223 224	memset(tpath, 0, sizeof(tpath));225	if (get_exec_path(tpath, sizeof(tpath))) {226		p_err("failed to create disassembler (get_exec_path)");227		return -1;228	}229 230	ctx->bfdf = bfd_openr(tpath, NULL);231	if (!ctx->bfdf) {232		p_err("failed to create disassembler (bfd_openr)");233		return -1;234	}235	if (!bfd_check_format(ctx->bfdf, bfd_object)) {236		p_err("failed to create disassembler (bfd_check_format)");237		goto err_close;238	}239	bfdf = ctx->bfdf;240 241	ctx->info = malloc(sizeof(struct disassemble_info));242	if (!ctx->info) {243		p_err("mem alloc failed");244		goto err_close;245	}246	info = ctx->info;247 248	if (json_output)249		init_disassemble_info_compat(info, stdout,250					     (fprintf_ftype) fprintf_json,251					     fprintf_json_styled);252	else253		init_disassemble_info_compat(info, stdout,254					     (fprintf_ftype) fprintf,255					     fprintf_styled);256 257	/* Update architecture info for offload. */258	if (arch) {259		const bfd_arch_info_type *inf = bfd_scan_arch(arch);260 261		if (inf) {262			bfdf->arch_info = inf;263		} else {264			p_err("No libbfd support for %s", arch);265			goto err_free;266		}267	}268 269	info->arch = bfd_get_arch(bfdf);270	info->mach = bfd_get_mach(bfdf);271	if (disassembler_options)272		info->disassembler_options = disassembler_options;273	info->buffer = image;274	info->buffer_length = len;275 276	disassemble_init_for_target(info);277 278#ifdef DISASM_FOUR_ARGS_SIGNATURE279	ctx->disassemble = disassembler(info->arch,280					bfd_big_endian(bfdf),281					info->mach,282					bfdf);283#else284	ctx->disassemble = disassembler(bfdf);285#endif286	if (!ctx->disassemble) {287		p_err("failed to create disassembler");288		goto err_free;289	}290	return 0;291 292err_free:293	free(info);294err_close:295	bfd_close(ctx->bfdf);296	return -1;297}298 299static void destroy_context(disasm_ctx_t *ctx)300{301	free(ctx->info);302	bfd_close(ctx->bfdf);303}304 305static int306disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image,307		 __maybe_unused ssize_t len, int pc)308{309	return ctx->disassemble(pc, ctx->info);310}311 312int disasm_init(void)313{314	bfd_init();315	return 0;316}317#endif /* HAVE_LIBBPFD_SUPPORT */318 319int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,320		      const char *arch, const char *disassembler_options,321		      const struct btf *btf,322		      const struct bpf_prog_linfo *prog_linfo,323		      __u64 func_ksym, unsigned int func_idx,324		      bool linum)325{326	const struct bpf_line_info *linfo = NULL;327	unsigned int nr_skip = 0;328	int count, i, pc = 0;329	disasm_ctx_t ctx;330 331	if (!len)332		return -1;333 334	if (init_context(&ctx, arch, disassembler_options, image, len))335		return -1;336 337	if (json_output)338		jsonw_start_array(json_wtr);339	do {340		if (prog_linfo) {341			linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,342								func_ksym + pc,343								func_idx,344								nr_skip);345			if (linfo)346				nr_skip++;347		}348 349		if (json_output) {350			jsonw_start_object(json_wtr);351			oper_count = 0;352			if (linfo)353				btf_dump_linfo_json(btf, linfo, linum);354			jsonw_name(json_wtr, "pc");355			jsonw_printf(json_wtr, "\"0x%x\"", pc);356		} else {357			if (linfo)358				btf_dump_linfo_plain(btf, linfo, "; ",359						     linum);360			printf("%4x:" DISASM_SPACER, pc);361		}362 363		count = disassemble_insn(&ctx, image, len, pc);364 365		if (json_output) {366			/* Operand array, was started in fprintf_json. Before367			 * that, make sure we have a _null_ value if no operand368			 * other than operation code was present.369			 */370			if (oper_count == 1)371				jsonw_null(json_wtr);372			jsonw_end_array(json_wtr);373		}374 375		if (opcodes) {376			if (json_output) {377				jsonw_name(json_wtr, "opcodes");378				jsonw_start_array(json_wtr);379				for (i = 0; i < count; ++i)380					jsonw_printf(json_wtr, "\"0x%02hhx\"",381						     (uint8_t)image[pc + i]);382				jsonw_end_array(json_wtr);383			} else {384				printf("\n\t");385				for (i = 0; i < count; ++i)386					printf("%02x ",387					       (uint8_t)image[pc + i]);388			}389		}390		if (json_output)391			jsonw_end_object(json_wtr);392		else393			printf("\n");394 395		pc += count;396	} while (count > 0 && pc < len);397	if (json_output)398		jsonw_end_array(json_wtr);399 400	destroy_context(&ctx);401 402	return 0;403}404