162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
262306a36Sopenharmony_ci/* Copyright (C) 2018 Netronome Systems, Inc. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#ifndef _GNU_SOURCE
562306a36Sopenharmony_ci#define _GNU_SOURCE
662306a36Sopenharmony_ci#endif
762306a36Sopenharmony_ci#include <stdarg.h>
862306a36Sopenharmony_ci#include <stdio.h>
962306a36Sopenharmony_ci#include <stdlib.h>
1062306a36Sopenharmony_ci#include <string.h>
1162306a36Sopenharmony_ci#include <sys/types.h>
1262306a36Sopenharmony_ci#include <bpf/libbpf.h>
1362306a36Sopenharmony_ci#include <bpf/libbpf_internal.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "disasm.h"
1662306a36Sopenharmony_ci#include "json_writer.h"
1762306a36Sopenharmony_ci#include "main.h"
1862306a36Sopenharmony_ci#include "xlated_dumper.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic int kernel_syms_cmp(const void *sym_a, const void *sym_b)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	return ((struct kernel_sym *)sym_a)->address -
2362306a36Sopenharmony_ci	       ((struct kernel_sym *)sym_b)->address;
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_civoid kernel_syms_load(struct dump_data *dd)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct kernel_sym *sym;
2962306a36Sopenharmony_ci	char buff[256];
3062306a36Sopenharmony_ci	void *tmp, *address;
3162306a36Sopenharmony_ci	FILE *fp;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	fp = fopen("/proc/kallsyms", "r");
3462306a36Sopenharmony_ci	if (!fp)
3562306a36Sopenharmony_ci		return;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	while (fgets(buff, sizeof(buff), fp)) {
3862306a36Sopenharmony_ci		tmp = libbpf_reallocarray(dd->sym_mapping, dd->sym_count + 1,
3962306a36Sopenharmony_ci					  sizeof(*dd->sym_mapping));
4062306a36Sopenharmony_ci		if (!tmp) {
4162306a36Sopenharmony_ciout:
4262306a36Sopenharmony_ci			free(dd->sym_mapping);
4362306a36Sopenharmony_ci			dd->sym_mapping = NULL;
4462306a36Sopenharmony_ci			fclose(fp);
4562306a36Sopenharmony_ci			return;
4662306a36Sopenharmony_ci		}
4762306a36Sopenharmony_ci		dd->sym_mapping = tmp;
4862306a36Sopenharmony_ci		sym = &dd->sym_mapping[dd->sym_count];
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci		/* module is optional */
5162306a36Sopenharmony_ci		sym->module[0] = '\0';
5262306a36Sopenharmony_ci		/* trim the square brackets around the module name */
5362306a36Sopenharmony_ci		if (sscanf(buff, "%p %*c %s [%[^]]s", &address, sym->name, sym->module) < 2)
5462306a36Sopenharmony_ci			continue;
5562306a36Sopenharmony_ci		sym->address = (unsigned long)address;
5662306a36Sopenharmony_ci		if (!strcmp(sym->name, "__bpf_call_base")) {
5762306a36Sopenharmony_ci			dd->address_call_base = sym->address;
5862306a36Sopenharmony_ci			/* sysctl kernel.kptr_restrict was set */
5962306a36Sopenharmony_ci			if (!sym->address)
6062306a36Sopenharmony_ci				goto out;
6162306a36Sopenharmony_ci		}
6262306a36Sopenharmony_ci		if (sym->address)
6362306a36Sopenharmony_ci			dd->sym_count++;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	fclose(fp);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	qsort(dd->sym_mapping, dd->sym_count,
6962306a36Sopenharmony_ci	      sizeof(*dd->sym_mapping), kernel_syms_cmp);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_civoid kernel_syms_destroy(struct dump_data *dd)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	free(dd->sym_mapping);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistruct kernel_sym *kernel_syms_search(struct dump_data *dd,
7862306a36Sopenharmony_ci				      unsigned long key)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct kernel_sym sym = {
8162306a36Sopenharmony_ci		.address = key,
8262306a36Sopenharmony_ci	};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return dd->sym_mapping ?
8562306a36Sopenharmony_ci	       bsearch(&sym, dd->sym_mapping, dd->sym_count,
8662306a36Sopenharmony_ci		       sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void __printf(2, 3) print_insn(void *private_data, const char *fmt, ...)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	va_list args;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	va_start(args, fmt);
9462306a36Sopenharmony_ci	vprintf(fmt, args);
9562306a36Sopenharmony_ci	va_end(args);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void __printf(2, 3)
9962306a36Sopenharmony_ciprint_insn_for_graph(void *private_data, const char *fmt, ...)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	char buf[64], *p;
10262306a36Sopenharmony_ci	va_list args;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	va_start(args, fmt);
10562306a36Sopenharmony_ci	vsnprintf(buf, sizeof(buf), fmt, args);
10662306a36Sopenharmony_ci	va_end(args);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	p = buf;
10962306a36Sopenharmony_ci	while (*p != '\0') {
11062306a36Sopenharmony_ci		if (*p == '\n') {
11162306a36Sopenharmony_ci			memmove(p + 3, p, strlen(buf) + 1 - (p - buf));
11262306a36Sopenharmony_ci			/* Align each instruction dump row left. */
11362306a36Sopenharmony_ci			*p++ = '\\';
11462306a36Sopenharmony_ci			*p++ = 'l';
11562306a36Sopenharmony_ci			/* Output multiline concatenation. */
11662306a36Sopenharmony_ci			*p++ = '\\';
11762306a36Sopenharmony_ci		} else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') {
11862306a36Sopenharmony_ci			memmove(p + 1, p, strlen(buf) + 1 - (p - buf));
11962306a36Sopenharmony_ci			/* Escape special character. */
12062306a36Sopenharmony_ci			*p++ = '\\';
12162306a36Sopenharmony_ci		}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		p++;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	printf("%s", buf);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic void __printf(2, 3)
13062306a36Sopenharmony_ciprint_insn_json(void *private_data, const char *fmt, ...)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	unsigned int l = strlen(fmt);
13362306a36Sopenharmony_ci	char chomped_fmt[l];
13462306a36Sopenharmony_ci	va_list args;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	va_start(args, fmt);
13762306a36Sopenharmony_ci	if (l > 0) {
13862306a36Sopenharmony_ci		strncpy(chomped_fmt, fmt, l - 1);
13962306a36Sopenharmony_ci		chomped_fmt[l - 1] = '\0';
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci	jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
14262306a36Sopenharmony_ci	va_end(args);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic const char *print_call_pcrel(struct dump_data *dd,
14662306a36Sopenharmony_ci				    struct kernel_sym *sym,
14762306a36Sopenharmony_ci				    unsigned long address,
14862306a36Sopenharmony_ci				    const struct bpf_insn *insn)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	if (!dd->nr_jited_ksyms)
15162306a36Sopenharmony_ci		/* Do not show address for interpreted programs */
15262306a36Sopenharmony_ci		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
15362306a36Sopenharmony_ci			"%+d", insn->off);
15462306a36Sopenharmony_ci	else if (sym)
15562306a36Sopenharmony_ci		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
15662306a36Sopenharmony_ci			 "%+d#%s", insn->off, sym->name);
15762306a36Sopenharmony_ci	else
15862306a36Sopenharmony_ci		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
15962306a36Sopenharmony_ci			 "%+d#0x%lx", insn->off, address);
16062306a36Sopenharmony_ci	return dd->scratch_buff;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic const char *print_call_helper(struct dump_data *dd,
16462306a36Sopenharmony_ci				     struct kernel_sym *sym,
16562306a36Sopenharmony_ci				     unsigned long address)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	if (sym)
16862306a36Sopenharmony_ci		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
16962306a36Sopenharmony_ci			 "%s", sym->name);
17062306a36Sopenharmony_ci	else
17162306a36Sopenharmony_ci		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
17262306a36Sopenharmony_ci			 "0x%lx", address);
17362306a36Sopenharmony_ci	return dd->scratch_buff;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic const char *print_call(void *private_data,
17762306a36Sopenharmony_ci			      const struct bpf_insn *insn)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct dump_data *dd = private_data;
18062306a36Sopenharmony_ci	unsigned long address = dd->address_call_base + insn->imm;
18162306a36Sopenharmony_ci	struct kernel_sym *sym;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (insn->src_reg == BPF_PSEUDO_CALL &&
18462306a36Sopenharmony_ci	    (__u32) insn->imm < dd->nr_jited_ksyms && dd->jited_ksyms)
18562306a36Sopenharmony_ci		address = dd->jited_ksyms[insn->imm];
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	sym = kernel_syms_search(dd, address);
18862306a36Sopenharmony_ci	if (insn->src_reg == BPF_PSEUDO_CALL)
18962306a36Sopenharmony_ci		return print_call_pcrel(dd, sym, address, insn);
19062306a36Sopenharmony_ci	else
19162306a36Sopenharmony_ci		return print_call_helper(dd, sym, address);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic const char *print_imm(void *private_data,
19562306a36Sopenharmony_ci			     const struct bpf_insn *insn,
19662306a36Sopenharmony_ci			     __u64 full_imm)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct dump_data *dd = private_data;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (insn->src_reg == BPF_PSEUDO_MAP_FD)
20162306a36Sopenharmony_ci		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
20262306a36Sopenharmony_ci			 "map[id:%u]", insn->imm);
20362306a36Sopenharmony_ci	else if (insn->src_reg == BPF_PSEUDO_MAP_VALUE)
20462306a36Sopenharmony_ci		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
20562306a36Sopenharmony_ci			 "map[id:%u][0]+%u", insn->imm, (insn + 1)->imm);
20662306a36Sopenharmony_ci	else if (insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE)
20762306a36Sopenharmony_ci		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
20862306a36Sopenharmony_ci			 "map[idx:%u]+%u", insn->imm, (insn + 1)->imm);
20962306a36Sopenharmony_ci	else if (insn->src_reg == BPF_PSEUDO_FUNC)
21062306a36Sopenharmony_ci		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
21162306a36Sopenharmony_ci			 "subprog[%+d]", insn->imm);
21262306a36Sopenharmony_ci	else
21362306a36Sopenharmony_ci		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
21462306a36Sopenharmony_ci			 "0x%llx", (unsigned long long)full_imm);
21562306a36Sopenharmony_ci	return dd->scratch_buff;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_civoid dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
21962306a36Sopenharmony_ci		      bool opcodes, bool linum)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
22262306a36Sopenharmony_ci	const struct bpf_insn_cbs cbs = {
22362306a36Sopenharmony_ci		.cb_print	= print_insn_json,
22462306a36Sopenharmony_ci		.cb_call	= print_call,
22562306a36Sopenharmony_ci		.cb_imm		= print_imm,
22662306a36Sopenharmony_ci		.private_data	= dd,
22762306a36Sopenharmony_ci	};
22862306a36Sopenharmony_ci	struct bpf_func_info *record;
22962306a36Sopenharmony_ci	struct bpf_insn *insn = buf;
23062306a36Sopenharmony_ci	struct btf *btf = dd->btf;
23162306a36Sopenharmony_ci	bool double_insn = false;
23262306a36Sopenharmony_ci	unsigned int nr_skip = 0;
23362306a36Sopenharmony_ci	char func_sig[1024];
23462306a36Sopenharmony_ci	unsigned int i;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	jsonw_start_array(json_wtr);
23762306a36Sopenharmony_ci	record = dd->func_info;
23862306a36Sopenharmony_ci	for (i = 0; i < len / sizeof(*insn); i++) {
23962306a36Sopenharmony_ci		if (double_insn) {
24062306a36Sopenharmony_ci			double_insn = false;
24162306a36Sopenharmony_ci			continue;
24262306a36Sopenharmony_ci		}
24362306a36Sopenharmony_ci		double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		jsonw_start_object(json_wtr);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		if (btf && record) {
24862306a36Sopenharmony_ci			if (record->insn_off == i) {
24962306a36Sopenharmony_ci				btf_dumper_type_only(btf, record->type_id,
25062306a36Sopenharmony_ci						     func_sig,
25162306a36Sopenharmony_ci						     sizeof(func_sig));
25262306a36Sopenharmony_ci				if (func_sig[0] != '\0') {
25362306a36Sopenharmony_ci					jsonw_name(json_wtr, "proto");
25462306a36Sopenharmony_ci					jsonw_string(json_wtr, func_sig);
25562306a36Sopenharmony_ci				}
25662306a36Sopenharmony_ci				record = (void *)record + dd->finfo_rec_size;
25762306a36Sopenharmony_ci			}
25862306a36Sopenharmony_ci		}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		if (prog_linfo) {
26162306a36Sopenharmony_ci			const struct bpf_line_info *linfo;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci			linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
26462306a36Sopenharmony_ci			if (linfo) {
26562306a36Sopenharmony_ci				btf_dump_linfo_json(btf, linfo, linum);
26662306a36Sopenharmony_ci				nr_skip++;
26762306a36Sopenharmony_ci			}
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		jsonw_name(json_wtr, "disasm");
27162306a36Sopenharmony_ci		print_bpf_insn(&cbs, insn + i, true);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		if (opcodes) {
27462306a36Sopenharmony_ci			jsonw_name(json_wtr, "opcodes");
27562306a36Sopenharmony_ci			jsonw_start_object(json_wtr);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci			jsonw_name(json_wtr, "code");
27862306a36Sopenharmony_ci			jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci			jsonw_name(json_wtr, "src_reg");
28162306a36Sopenharmony_ci			jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci			jsonw_name(json_wtr, "dst_reg");
28462306a36Sopenharmony_ci			jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci			jsonw_name(json_wtr, "off");
28762306a36Sopenharmony_ci			print_hex_data_json((uint8_t *)(&insn[i].off), 2);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci			jsonw_name(json_wtr, "imm");
29062306a36Sopenharmony_ci			if (double_insn && i < len - 1)
29162306a36Sopenharmony_ci				print_hex_data_json((uint8_t *)(&insn[i].imm),
29262306a36Sopenharmony_ci						    12);
29362306a36Sopenharmony_ci			else
29462306a36Sopenharmony_ci				print_hex_data_json((uint8_t *)(&insn[i].imm),
29562306a36Sopenharmony_ci						    4);
29662306a36Sopenharmony_ci			jsonw_end_object(json_wtr);
29762306a36Sopenharmony_ci		}
29862306a36Sopenharmony_ci		jsonw_end_object(json_wtr);
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci	jsonw_end_array(json_wtr);
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_civoid dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
30462306a36Sopenharmony_ci		       bool opcodes, bool linum)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
30762306a36Sopenharmony_ci	const struct bpf_insn_cbs cbs = {
30862306a36Sopenharmony_ci		.cb_print	= print_insn,
30962306a36Sopenharmony_ci		.cb_call	= print_call,
31062306a36Sopenharmony_ci		.cb_imm		= print_imm,
31162306a36Sopenharmony_ci		.private_data	= dd,
31262306a36Sopenharmony_ci	};
31362306a36Sopenharmony_ci	struct bpf_func_info *record;
31462306a36Sopenharmony_ci	struct bpf_insn *insn = buf;
31562306a36Sopenharmony_ci	struct btf *btf = dd->btf;
31662306a36Sopenharmony_ci	unsigned int nr_skip = 0;
31762306a36Sopenharmony_ci	bool double_insn = false;
31862306a36Sopenharmony_ci	char func_sig[1024];
31962306a36Sopenharmony_ci	unsigned int i;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	record = dd->func_info;
32262306a36Sopenharmony_ci	for (i = 0; i < len / sizeof(*insn); i++) {
32362306a36Sopenharmony_ci		if (double_insn) {
32462306a36Sopenharmony_ci			double_insn = false;
32562306a36Sopenharmony_ci			continue;
32662306a36Sopenharmony_ci		}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci		if (btf && record) {
32962306a36Sopenharmony_ci			if (record->insn_off == i) {
33062306a36Sopenharmony_ci				btf_dumper_type_only(btf, record->type_id,
33162306a36Sopenharmony_ci						     func_sig,
33262306a36Sopenharmony_ci						     sizeof(func_sig));
33362306a36Sopenharmony_ci				if (func_sig[0] != '\0')
33462306a36Sopenharmony_ci					printf("%s:\n", func_sig);
33562306a36Sopenharmony_ci				record = (void *)record + dd->finfo_rec_size;
33662306a36Sopenharmony_ci			}
33762306a36Sopenharmony_ci		}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		if (prog_linfo) {
34062306a36Sopenharmony_ci			const struct bpf_line_info *linfo;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci			linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
34362306a36Sopenharmony_ci			if (linfo) {
34462306a36Sopenharmony_ci				btf_dump_linfo_plain(btf, linfo, "; ",
34562306a36Sopenharmony_ci						     linum);
34662306a36Sopenharmony_ci				nr_skip++;
34762306a36Sopenharmony_ci			}
34862306a36Sopenharmony_ci		}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci		double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci		printf("% 4d: ", i);
35362306a36Sopenharmony_ci		print_bpf_insn(&cbs, insn + i, true);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		if (opcodes) {
35662306a36Sopenharmony_ci			printf("       ");
35762306a36Sopenharmony_ci			fprint_hex(stdout, insn + i, 8, " ");
35862306a36Sopenharmony_ci			if (double_insn && i < len - 1) {
35962306a36Sopenharmony_ci				printf(" ");
36062306a36Sopenharmony_ci				fprint_hex(stdout, insn + i + 1, 8, " ");
36162306a36Sopenharmony_ci			}
36262306a36Sopenharmony_ci			printf("\n");
36362306a36Sopenharmony_ci		}
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_civoid dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
36862306a36Sopenharmony_ci			   unsigned int start_idx,
36962306a36Sopenharmony_ci			   bool opcodes, bool linum)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	const struct bpf_insn_cbs cbs = {
37262306a36Sopenharmony_ci		.cb_print	= print_insn_for_graph,
37362306a36Sopenharmony_ci		.cb_call	= print_call,
37462306a36Sopenharmony_ci		.cb_imm		= print_imm,
37562306a36Sopenharmony_ci		.private_data	= dd,
37662306a36Sopenharmony_ci	};
37762306a36Sopenharmony_ci	const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
37862306a36Sopenharmony_ci	const struct bpf_line_info *last_linfo = NULL;
37962306a36Sopenharmony_ci	struct bpf_func_info *record = dd->func_info;
38062306a36Sopenharmony_ci	struct bpf_insn *insn_start = buf_start;
38162306a36Sopenharmony_ci	struct bpf_insn *insn_end = buf_end;
38262306a36Sopenharmony_ci	struct bpf_insn *cur = insn_start;
38362306a36Sopenharmony_ci	struct btf *btf = dd->btf;
38462306a36Sopenharmony_ci	bool double_insn = false;
38562306a36Sopenharmony_ci	char func_sig[1024];
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	for (; cur <= insn_end; cur++) {
38862306a36Sopenharmony_ci		unsigned int insn_off;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		if (double_insn) {
39162306a36Sopenharmony_ci			double_insn = false;
39262306a36Sopenharmony_ci			continue;
39362306a36Sopenharmony_ci		}
39462306a36Sopenharmony_ci		double_insn = cur->code == (BPF_LD | BPF_IMM | BPF_DW);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci		insn_off = (unsigned int)(cur - insn_start + start_idx);
39762306a36Sopenharmony_ci		if (btf && record) {
39862306a36Sopenharmony_ci			if (record->insn_off == insn_off) {
39962306a36Sopenharmony_ci				btf_dumper_type_only(btf, record->type_id,
40062306a36Sopenharmony_ci						     func_sig,
40162306a36Sopenharmony_ci						     sizeof(func_sig));
40262306a36Sopenharmony_ci				if (func_sig[0] != '\0')
40362306a36Sopenharmony_ci					printf("; %s:\\l\\\n", func_sig);
40462306a36Sopenharmony_ci				record = (void *)record + dd->finfo_rec_size;
40562306a36Sopenharmony_ci			}
40662306a36Sopenharmony_ci		}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		if (prog_linfo) {
40962306a36Sopenharmony_ci			const struct bpf_line_info *linfo;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci			linfo = bpf_prog_linfo__lfind(prog_linfo, insn_off, 0);
41262306a36Sopenharmony_ci			if (linfo && linfo != last_linfo) {
41362306a36Sopenharmony_ci				btf_dump_linfo_dotlabel(btf, linfo, linum);
41462306a36Sopenharmony_ci				last_linfo = linfo;
41562306a36Sopenharmony_ci			}
41662306a36Sopenharmony_ci		}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci		printf("%d: ", insn_off);
41962306a36Sopenharmony_ci		print_bpf_insn(&cbs, cur, true);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		if (opcodes) {
42262306a36Sopenharmony_ci			printf("\\ \\ \\ \\ ");
42362306a36Sopenharmony_ci			fprint_hex(stdout, cur, 8, " ");
42462306a36Sopenharmony_ci			if (double_insn && cur <= insn_end - 1) {
42562306a36Sopenharmony_ci				printf(" ");
42662306a36Sopenharmony_ci				fprint_hex(stdout, cur + 1, 8, " ");
42762306a36Sopenharmony_ci			}
42862306a36Sopenharmony_ci			printf("\\l\\\n");
42962306a36Sopenharmony_ci		}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		if (cur != insn_end)
43262306a36Sopenharmony_ci			printf("| ");
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci}
435