brintos

brintos / linux-shallow public Read only

0
0
Text · 6.1 KiB · febd6b1 Raw
246 lines · c
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_SUPPORT7 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 test14 * programs written in BPF assembly, thus assume that 32 local labels15 * would be sufficient.16 */17#define MAX_LOCAL_LABELS 3218 19/* Local labels are encoded as 'L42', this requires 4 bytes of storage:20 * 3 characters + zero byte21 */22#define LOCAL_LABEL_LEN 423 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 or46	 * 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 targets50	 *   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 assumes127		 * 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 */246