Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
  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	     __maybe_unused __u64 func_ksym)
 85{
 86	char *triple;
 87
 88	if (arch)
 89		triple = LLVMNormalizeTargetTriple(arch);
 90	else
 91		triple = LLVMGetDefaultTargetTriple();
 92	if (!triple) {
 93		p_err("Failed to retrieve triple");
 94		return -1;
 95	}
 96	*ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback);
 97	LLVMDisposeMessage(triple);
 98
 99	if (!*ctx) {
100		p_err("Failed to create disassembler");
101		return -1;
102	}
103
104	return 0;
105}
106
107static void destroy_context(disasm_ctx_t *ctx)
108{
109	LLVMDisposeMessage(*ctx);
110}
111
112static int
113disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc,
114		 __u64 func_ksym)
115{
116	char buf[256];
117	int count;
118
119	count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, func_ksym + pc,
120				      buf, sizeof(buf));
121	if (json_output)
122		printf_json(buf);
123	else
124		printf("%s", buf);
125
126	return count;
127}
128
129int disasm_init(void)
130{
131	LLVMInitializeAllTargetInfos();
132	LLVMInitializeAllTargetMCs();
133	LLVMInitializeAllDisassemblers();
134	return 0;
135}
136#endif /* HAVE_LLVM_SUPPORT */
137
138#ifdef HAVE_LIBBFD_SUPPORT
139#define DISASM_SPACER "\t"
140
141struct disasm_info {
142	struct disassemble_info info;
143	__u64 func_ksym;
144};
145
146static void disasm_print_addr(bfd_vma addr, struct disassemble_info *info)
147{
148	struct disasm_info *dinfo = container_of(info, struct disasm_info, info);
149
150	addr += dinfo->func_ksym;
151	generic_print_address(addr, info);
152}
153
154typedef struct {
155	struct disasm_info *info;
156	disassembler_ftype disassemble;
157	bfd *bfdf;
158} disasm_ctx_t;
159
160static int get_exec_path(char *tpath, size_t size)
161{
162	const char *path = "/proc/self/exe";
163	ssize_t len;
164
165	len = readlink(path, tpath, size - 1);
166	if (len <= 0)
167		return -1;
168
169	tpath[len] = 0;
170
171	return 0;
172}
173
174static int printf_json(void *out, const char *fmt, va_list ap)
175{
176	char *s;
177	int err;
178
179	err = vasprintf(&s, fmt, ap);
180	if (err < 0)
181		return -1;
182
183	if (!oper_count) {
184		int i;
185
186		/* Strip trailing spaces */
187		i = strlen(s) - 1;
188		while (s[i] == ' ')
189			s[i--] = '\0';
190
191		jsonw_string_field(json_wtr, "operation", s);
192		jsonw_name(json_wtr, "operands");
193		jsonw_start_array(json_wtr);
194		oper_count++;
195	} else if (!strcmp(fmt, ",")) {
196		   /* Skip */
197	} else {
198		jsonw_string(json_wtr, s);
199		oper_count++;
200	}
201	free(s);
202	return 0;
203}
204
205static int fprintf_json(void *out, const char *fmt, ...)
206{
207	va_list ap;
208	int r;
209
210	va_start(ap, fmt);
211	r = printf_json(out, fmt, ap);
212	va_end(ap);
213
214	return r;
215}
216
217static int fprintf_json_styled(void *out,
218			       enum disassembler_style style __maybe_unused,
219			       const char *fmt, ...)
220{
221	va_list ap;
222	int r;
223
224	va_start(ap, fmt);
225	r = printf_json(out, fmt, ap);
226	va_end(ap);
227
228	return r;
229}
230
231static int init_context(disasm_ctx_t *ctx, const char *arch,
232			const char *disassembler_options,
233			unsigned char *image, ssize_t len, __u64 func_ksym)
234{
235	struct disassemble_info *info;
236	char tpath[PATH_MAX];
237	bfd *bfdf;
238
239	memset(tpath, 0, sizeof(tpath));
240	if (get_exec_path(tpath, sizeof(tpath))) {
241		p_err("failed to create disassembler (get_exec_path)");
242		return -1;
243	}
244
245	ctx->bfdf = bfd_openr(tpath, NULL);
246	if (!ctx->bfdf) {
247		p_err("failed to create disassembler (bfd_openr)");
248		return -1;
249	}
250	if (!bfd_check_format(ctx->bfdf, bfd_object)) {
251		p_err("failed to create disassembler (bfd_check_format)");
252		goto err_close;
253	}
254	bfdf = ctx->bfdf;
255
256	ctx->info = malloc(sizeof(struct disasm_info));
257	if (!ctx->info) {
258		p_err("mem alloc failed");
259		goto err_close;
260	}
261	ctx->info->func_ksym = func_ksym;
262	info = &ctx->info->info;
263
264	if (json_output)
265		init_disassemble_info_compat(info, stdout,
266					     (fprintf_ftype) fprintf_json,
267					     fprintf_json_styled);
268	else
269		init_disassemble_info_compat(info, stdout,
270					     (fprintf_ftype) fprintf,
271					     fprintf_styled);
272
273	/* Update architecture info for offload. */
274	if (arch) {
275		const bfd_arch_info_type *inf = bfd_scan_arch(arch);
276
277		if (inf) {
278			bfdf->arch_info = inf;
279		} else {
280			p_err("No libbfd support for %s", arch);
281			goto err_free;
282		}
283	}
284
285	info->arch = bfd_get_arch(bfdf);
286	info->mach = bfd_get_mach(bfdf);
287	if (disassembler_options)
288		info->disassembler_options = disassembler_options;
289	info->buffer = image;
290	info->buffer_length = len;
291	info->print_address_func = disasm_print_addr;
292
293	disassemble_init_for_target(info);
294
295#ifdef DISASM_FOUR_ARGS_SIGNATURE
296	ctx->disassemble = disassembler(info->arch,
297					bfd_big_endian(bfdf),
298					info->mach,
299					bfdf);
300#else
301	ctx->disassemble = disassembler(bfdf);
302#endif
303	if (!ctx->disassemble) {
304		p_err("failed to create disassembler");
305		goto err_free;
306	}
307	return 0;
308
309err_free:
310	free(info);
311err_close:
312	bfd_close(ctx->bfdf);
313	return -1;
314}
315
316static void destroy_context(disasm_ctx_t *ctx)
317{
318	free(ctx->info);
319	bfd_close(ctx->bfdf);
320}
321
322static int
323disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image,
324		 __maybe_unused ssize_t len, int pc,
325		 __maybe_unused __u64 func_ksym)
326{
327	return ctx->disassemble(pc, &ctx->info->info);
328}
329
330int disasm_init(void)
331{
332	bfd_init();
333	return 0;
334}
335#endif /* HAVE_LIBBPFD_SUPPORT */
336
337int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
338		      const char *arch, const char *disassembler_options,
339		      const struct btf *btf,
340		      const struct bpf_prog_linfo *prog_linfo,
341		      __u64 func_ksym, unsigned int func_idx,
342		      bool linum)
343{
344	const struct bpf_line_info *linfo = NULL;
345	unsigned int nr_skip = 0;
346	int count, i, pc = 0;
347	disasm_ctx_t ctx;
348
349	if (!len)
350		return -1;
351
352	if (init_context(&ctx, arch, disassembler_options, image, len, func_ksym))
353		return -1;
354
355	if (json_output)
356		jsonw_start_array(json_wtr);
357	do {
358		if (prog_linfo) {
359			linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
360								func_ksym + pc,
361								func_idx,
362								nr_skip);
363			if (linfo)
364				nr_skip++;
365		}
366
367		if (json_output) {
368			jsonw_start_object(json_wtr);
369			oper_count = 0;
370			if (linfo)
371				btf_dump_linfo_json(btf, linfo, linum);
372			jsonw_name(json_wtr, "pc");
373			jsonw_printf(json_wtr, "\"0x%x\"", pc);
374		} else {
375			if (linfo)
376				btf_dump_linfo_plain(btf, linfo, "; ",
377						     linum);
378			printf("%4x:" DISASM_SPACER, pc);
379		}
380
381		count = disassemble_insn(&ctx, image, len, pc, func_ksym);
382
383		if (json_output) {
384			/* Operand array, was started in fprintf_json. Before
385			 * that, make sure we have a _null_ value if no operand
386			 * other than operation code was present.
387			 */
388			if (oper_count == 1)
389				jsonw_null(json_wtr);
390			jsonw_end_array(json_wtr);
391		}
392
393		if (opcodes) {
394			if (json_output) {
395				jsonw_name(json_wtr, "opcodes");
396				jsonw_start_array(json_wtr);
397				for (i = 0; i < count; ++i)
398					jsonw_printf(json_wtr, "\"0x%02hhx\"",
399						     (uint8_t)image[pc + i]);
400				jsonw_end_array(json_wtr);
401			} else {
402				printf("\n\t");
403				for (i = 0; i < count; ++i)
404					printf("%02x ",
405					       (uint8_t)image[pc + i]);
406			}
407		}
408		if (json_output)
409			jsonw_end_object(json_wtr);
410		else
411			printf("\n");
412
413		pc += count;
414	} while (count > 0 && pc < len);
415	if (json_output)
416		jsonw_end_array(json_wtr);
417
418	destroy_context(&ctx);
419
420	return 0;
421}