Linux Audio

Check our new training course

Loading...
v5.14.15
  1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
  2/*
  3 * Based on:
  4 *
  5 * Minimal BPF JIT image disassembler
  6 *
  7 * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
  8 * 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#define _GNU_SOURCE
 
 15#include <stdio.h>
 16#include <stdarg.h>
 17#include <stdint.h>
 18#include <stdlib.h>
 19#include <assert.h>
 20#include <unistd.h>
 21#include <string.h>
 22#include <bfd.h>
 23#include <dis-asm.h>
 24#include <sys/stat.h>
 25#include <limits.h>
 26#include <bpf/libbpf.h>
 27
 
 
 
 
 
 
 
 
 
 
 
 
 
 28#include "json_writer.h"
 29#include "main.h"
 30
 31static void get_exec_path(char *tpath, size_t size)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 32{
 33	const char *path = "/proc/self/exe";
 34	ssize_t len;
 35
 36	len = readlink(path, tpath, size - 1);
 37	assert(len > 0);
 
 
 38	tpath[len] = 0;
 
 
 39}
 40
 41static int oper_count;
 42static int fprintf_json(void *out, const char *fmt, ...)
 43{
 44	va_list ap;
 45	char *s;
 46	int err;
 47
 48	va_start(ap, fmt);
 49	err = vasprintf(&s, fmt, ap);
 50	va_end(ap);
 51	if (err < 0)
 52		return -1;
 53
 54	if (!oper_count) {
 55		int i;
 56
 57		/* Strip trailing spaces */
 58		i = strlen(s) - 1;
 59		while (s[i] == ' ')
 60			s[i--] = '\0';
 61
 62		jsonw_string_field(json_wtr, "operation", s);
 63		jsonw_name(json_wtr, "operands");
 64		jsonw_start_array(json_wtr);
 65		oper_count++;
 66	} else if (!strcmp(fmt, ",")) {
 67		   /* Skip */
 68	} else {
 69		jsonw_string(json_wtr, s);
 70		oper_count++;
 71	}
 72	free(s);
 73	return 0;
 74}
 75
 76void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
 77		       const char *arch, const char *disassembler_options,
 78		       const struct btf *btf,
 79		       const struct bpf_prog_linfo *prog_linfo,
 80		       __u64 func_ksym, unsigned int func_idx,
 81		       bool linum)
 82{
 83	const struct bpf_line_info *linfo = NULL;
 84	disassembler_ftype disassemble;
 85	struct disassemble_info info;
 86	unsigned int nr_skip = 0;
 87	int count, i, pc = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 88	char tpath[PATH_MAX];
 89	bfd *bfdf;
 90
 91	if (!len)
 92		return;
 93
 94	memset(tpath, 0, sizeof(tpath));
 95	get_exec_path(tpath, sizeof(tpath));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 96
 97	bfdf = bfd_openr(tpath, NULL);
 98	assert(bfdf);
 99	assert(bfd_check_format(bfdf, bfd_object));
 
 
 
100
101	if (json_output)
102		init_disassemble_info(&info, stdout,
103				      (fprintf_ftype) fprintf_json);
 
104	else
105		init_disassemble_info(&info, stdout,
106				      (fprintf_ftype) fprintf);
 
107
108	/* Update architecture info for offload. */
109	if (arch) {
110		const bfd_arch_info_type *inf = bfd_scan_arch(arch);
111
112		if (inf) {
113			bfdf->arch_info = inf;
114		} else {
115			p_err("No libbfd support for %s", arch);
116			return;
117		}
118	}
119
120	info.arch = bfd_get_arch(bfdf);
121	info.mach = bfd_get_mach(bfdf);
122	if (disassembler_options)
123		info.disassembler_options = disassembler_options;
124	info.buffer = image;
125	info.buffer_length = len;
126
127	disassemble_init_for_target(&info);
128
129#ifdef DISASM_FOUR_ARGS_SIGNATURE
130	disassemble = disassembler(info.arch,
131				   bfd_big_endian(bfdf),
132				   info.mach,
133				   bfdf);
134#else
135	disassemble = disassembler(bfdf);
136#endif
137	assert(disassemble);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
139	if (json_output)
140		jsonw_start_array(json_wtr);
141	do {
142		if (prog_linfo) {
143			linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
144								func_ksym + pc,
145								func_idx,
146								nr_skip);
147			if (linfo)
148				nr_skip++;
149		}
150
151		if (json_output) {
152			jsonw_start_object(json_wtr);
153			oper_count = 0;
154			if (linfo)
155				btf_dump_linfo_json(btf, linfo, linum);
156			jsonw_name(json_wtr, "pc");
157			jsonw_printf(json_wtr, "\"0x%x\"", pc);
158		} else {
159			if (linfo)
160				btf_dump_linfo_plain(btf, linfo, "; ",
161						     linum);
162			printf("%4x:\t", pc);
163		}
164
165		count = disassemble(pc, &info);
 
166		if (json_output) {
167			/* Operand array, was started in fprintf_json. Before
168			 * that, make sure we have a _null_ value if no operand
169			 * other than operation code was present.
170			 */
171			if (oper_count == 1)
172				jsonw_null(json_wtr);
173			jsonw_end_array(json_wtr);
174		}
175
176		if (opcodes) {
177			if (json_output) {
178				jsonw_name(json_wtr, "opcodes");
179				jsonw_start_array(json_wtr);
180				for (i = 0; i < count; ++i)
181					jsonw_printf(json_wtr, "\"0x%02hhx\"",
182						     (uint8_t)image[pc + i]);
183				jsonw_end_array(json_wtr);
184			} else {
185				printf("\n\t");
186				for (i = 0; i < count; ++i)
187					printf("%02x ",
188					       (uint8_t)image[pc + i]);
189			}
190		}
191		if (json_output)
192			jsonw_end_object(json_wtr);
193		else
194			printf("\n");
195
196		pc += count;
197	} while (count > 0 && pc < len);
198	if (json_output)
199		jsonw_end_array(json_wtr);
200
201	bfd_close(bfdf);
202}
203
204int disasm_init(void)
205{
206	bfd_init();
207	return 0;
208}
v6.2
  1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
  2/*
  3 * Based on:
  4 *
  5 * Minimal BPF JIT image disassembler
  6 *
  7 * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
  8 * 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_SOURCE
 15#define _GNU_SOURCE
 16#endif
 17#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_SUPPORT
 28#include <llvm-c/Core.h>
 29#include <llvm-c/Disassembler.h>
 30#include <llvm-c/Target.h>
 31#include <llvm-c/TargetMachine.h>
 32#endif
 33
 34#ifdef HAVE_LIBBFD_SUPPORT
 35#include <bfd.h>
 36#include <dis-asm.h>
 37#include <tools/dis-asm-compat.h>
 38#endif
 39
 40#include "json_writer.h"
 41#include "main.h"
 42
 43static int oper_count;
 44
 45#ifdef HAVE_LLVM_SUPPORT
 46#define DISASM_SPACER
 47
 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 disassembler
 67 * print PC-relative addresses instead of byte offsets for branch instruction
 68 * 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 int
 81init_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	else
 90		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 int
112disassemble_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	else
122		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_SUPPORT
137#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	else
253		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_SIGNATURE
279	ctx->disassemble = disassembler(info->arch,
280					bfd_big_endian(bfdf),
281					info->mach,
282					bfdf);
283#else
284	ctx->disassemble = disassembler(bfdf);
285#endif
286	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 int
306disassemble_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. Before
367			 * that, make sure we have a _null_ value if no operand
368			 * 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		else
393			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}