Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
  2#include <bpf/bpf.h>
  3#include <bpf/libbpf.h>
  4#include <test_progs.h>
  5
  6#ifdef HAVE_LLVM_SUPPORT
  7
  8#include <llvm-c/Core.h>
  9#include <llvm-c/Disassembler.h>
 10#include <llvm-c/Target.h>
 11#include <llvm-c/TargetMachine.h>
 12
 13/* The intent is to use get_jited_program_text() for small test
 14 * programs written in BPF assembly, thus assume that 32 local labels
 15 * would be sufficient.
 16 */
 17#define MAX_LOCAL_LABELS 32
 18
 19/* Local labels are encoded as 'L42', this requires 4 bytes of storage:
 20 * 3 characters + zero byte
 21 */
 22#define LOCAL_LABEL_LEN 4
 23
 24static bool llvm_initialized;
 25
 26struct local_labels {
 27	bool print_phase;
 28	__u32 prog_len;
 29	__u32 cnt;
 30	__u32 pcs[MAX_LOCAL_LABELS];
 31	char names[MAX_LOCAL_LABELS][LOCAL_LABEL_LEN];
 32};
 33
 34static const char *lookup_symbol(void *data, uint64_t ref_value, uint64_t *ref_type,
 35				 uint64_t ref_pc, const char **ref_name)
 36{
 37	struct local_labels *labels = data;
 38	uint64_t type = *ref_type;
 39	int i;
 40
 41	*ref_type = LLVMDisassembler_ReferenceType_InOut_None;
 42	*ref_name = NULL;
 43	if (type != LLVMDisassembler_ReferenceType_In_Branch)
 44		return NULL;
 45	/* Depending on labels->print_phase either discover local labels or
 46	 * return a name assigned with local jump target:
 47	 * - if print_phase is true and ref_value is in labels->pcs,
 48	 *   return corresponding labels->name.
 49	 * - if print_phase is false, save program-local jump targets
 50	 *   in labels->pcs;
 51	 */
 52	if (labels->print_phase) {
 53		for (i = 0; i < labels->cnt; ++i)
 54			if (labels->pcs[i] == ref_value)
 55				return labels->names[i];
 56	} else {
 57		if (labels->cnt < MAX_LOCAL_LABELS && ref_value < labels->prog_len)
 58			labels->pcs[labels->cnt++] = ref_value;
 59	}
 60	return NULL;
 61}
 62
 63static int disasm_insn(LLVMDisasmContextRef ctx, uint8_t *image, __u32 len, __u32 pc,
 64		       char *buf, __u32 buf_sz)
 65{
 66	int i, cnt;
 67
 68	cnt = LLVMDisasmInstruction(ctx, image + pc, len - pc, pc,
 69				    buf, buf_sz);
 70	if (cnt > 0)
 71		return cnt;
 72	PRINT_FAIL("Can't disasm instruction at offset %d:", pc);
 73	for (i = 0; i < 16 && pc + i < len; ++i)
 74		printf(" %02x", image[pc + i]);
 75	printf("\n");
 76	return -EINVAL;
 77}
 78
 79static int cmp_u32(const void *_a, const void *_b)
 80{
 81	__u32 a = *(__u32 *)_a;
 82	__u32 b = *(__u32 *)_b;
 83
 84	if (a < b)
 85		return -1;
 86	if (a > b)
 87		return 1;
 88	return 0;
 89}
 90
 91static int disasm_one_func(FILE *text_out, uint8_t *image, __u32 len)
 92{
 93	char *label, *colon, *triple = NULL;
 94	LLVMDisasmContextRef ctx = NULL;
 95	struct local_labels labels = {};
 96	__u32 *label_pc, pc;
 97	int i, cnt, err = 0;
 98	char buf[64];
 99
100	triple = LLVMGetDefaultTargetTriple();
101	ctx = LLVMCreateDisasm(triple, &labels, 0, NULL, lookup_symbol);
102	if (!ASSERT_OK_PTR(ctx, "LLVMCreateDisasm")) {
103		err = -EINVAL;
104		goto out;
105	}
106
107	cnt = LLVMSetDisasmOptions(ctx, LLVMDisassembler_Option_PrintImmHex);
108	if (!ASSERT_EQ(cnt, 1, "LLVMSetDisasmOptions")) {
109		err = -EINVAL;
110		goto out;
111	}
112
113	/* discover labels */
114	labels.prog_len = len;
115	pc = 0;
116	while (pc < len) {
117		cnt = disasm_insn(ctx, image, len, pc, buf, 1);
118		if (cnt < 0) {
119			err = cnt;
120			goto out;
121		}
122		pc += cnt;
123	}
124	qsort(labels.pcs, labels.cnt, sizeof(*labels.pcs), cmp_u32);
125	for (i = 0; i < labels.cnt; ++i)
126		/* gcc is unable to infer upper bound for labels.cnt and assumes
127		 * it to be U32_MAX. U32_MAX takes 10 decimal digits.
128		 * snprintf below prints into labels.names[*],
129		 * which has space only for two digits and a letter.
130		 * To avoid truncation warning use (i % MAX_LOCAL_LABELS),
131		 * which informs gcc about printed value upper bound.
132		 */
133		snprintf(labels.names[i], sizeof(labels.names[i]), "L%d", i % MAX_LOCAL_LABELS);
134
135	/* now print with labels */
136	labels.print_phase = true;
137	pc = 0;
138	while (pc < len) {
139		cnt = disasm_insn(ctx, image, len, pc, buf, sizeof(buf));
140		if (cnt < 0) {
141			err = cnt;
142			goto out;
143		}
144		label_pc = bsearch(&pc, labels.pcs, labels.cnt, sizeof(*labels.pcs), cmp_u32);
145		label = "";
146		colon = "";
147		if (label_pc) {
148			label = labels.names[label_pc - labels.pcs];
149			colon = ":";
150		}
151		fprintf(text_out, "%x:\t", pc);
152		for (i = 0; i < cnt; ++i)
153			fprintf(text_out, "%02x ", image[pc + i]);
154		for (i = cnt * 3; i < 12 * 3; ++i)
155			fputc(' ', text_out);
156		fprintf(text_out, "%s%s%s\n", label, colon, buf);
157		pc += cnt;
158	}
159
160out:
161	if (triple)
162		LLVMDisposeMessage(triple);
163	if (ctx)
164		LLVMDisasmDispose(ctx);
165	return err;
166}
167
168int get_jited_program_text(int fd, char *text, size_t text_sz)
169{
170	struct bpf_prog_info info = {};
171	__u32 info_len = sizeof(info);
172	__u32 jited_funcs, len, pc;
173	__u32 *func_lens = NULL;
174	FILE *text_out = NULL;
175	uint8_t *image = NULL;
176	int i, err = 0;
177
178	if (!llvm_initialized) {
179		LLVMInitializeAllTargetInfos();
180		LLVMInitializeAllTargetMCs();
181		LLVMInitializeAllDisassemblers();
182		llvm_initialized = 1;
183	}
184
185	text_out = fmemopen(text, text_sz, "w");
186	if (!ASSERT_OK_PTR(text_out, "open_memstream")) {
187		err = -errno;
188		goto out;
189	}
190
191	/* first call is to find out jited program len */
192	err = bpf_prog_get_info_by_fd(fd, &info, &info_len);
193	if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #1"))
194		goto out;
195
196	len = info.jited_prog_len;
197	image = malloc(len);
198	if (!ASSERT_OK_PTR(image, "malloc(info.jited_prog_len)")) {
199		err = -ENOMEM;
200		goto out;
201	}
202
203	jited_funcs = info.nr_jited_func_lens;
204	func_lens = malloc(jited_funcs * sizeof(__u32));
205	if (!ASSERT_OK_PTR(func_lens, "malloc(info.nr_jited_func_lens)")) {
206		err = -ENOMEM;
207		goto out;
208	}
209
210	memset(&info, 0, sizeof(info));
211	info.jited_prog_insns = (__u64)image;
212	info.jited_prog_len = len;
213	info.jited_func_lens = (__u64)func_lens;
214	info.nr_jited_func_lens = jited_funcs;
215	err = bpf_prog_get_info_by_fd(fd, &info, &info_len);
216	if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #2"))
217		goto out;
218
219	for (pc = 0, i = 0; i < jited_funcs; ++i) {
220		fprintf(text_out, "func #%d:\n", i);
221		disasm_one_func(text_out, image + pc, func_lens[i]);
222		fprintf(text_out, "\n");
223		pc += func_lens[i];
224	}
225
226out:
227	if (text_out)
228		fclose(text_out);
229	if (image)
230		free(image);
231	if (func_lens)
232		free(func_lens);
233	return err;
234}
235
236#else /* HAVE_LLVM_SUPPORT */
237
238int get_jited_program_text(int fd, char *text, size_t text_sz)
239{
240	if (env.verbosity >= VERBOSE_VERY)
241		printf("compiled w/o llvm development libraries, can't dis-assembly binary code");
242	return -EOPNOTSUPP;
243}
244
245#endif /* HAVE_LLVM_SUPPORT */