Path: blob/master/tools/testing/selftests/bpf/jit_disasm_helpers.c
26285 views
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)1#include <bpf/bpf.h>2#include <bpf/libbpf.h>3#include <test_progs.h>45#ifdef HAVE_LLVM_SUPPORT67#include <llvm-c/Core.h>8#include <llvm-c/Disassembler.h>9#include <llvm-c/Target.h>10#include <llvm-c/TargetMachine.h>1112/* The intent is to use get_jited_program_text() for small test13* programs written in BPF assembly, thus assume that 32 local labels14* would be sufficient.15*/16#define MAX_LOCAL_LABELS 321718/* Local labels are encoded as 'L42', this requires 4 bytes of storage:19* 3 characters + zero byte20*/21#define LOCAL_LABEL_LEN 42223static bool llvm_initialized;2425struct local_labels {26bool print_phase;27__u32 prog_len;28__u32 cnt;29__u32 pcs[MAX_LOCAL_LABELS];30char names[MAX_LOCAL_LABELS][LOCAL_LABEL_LEN];31};3233static const char *lookup_symbol(void *data, uint64_t ref_value, uint64_t *ref_type,34uint64_t ref_pc, const char **ref_name)35{36struct local_labels *labels = data;37uint64_t type = *ref_type;38int i;3940*ref_type = LLVMDisassembler_ReferenceType_InOut_None;41*ref_name = NULL;42if (type != LLVMDisassembler_ReferenceType_In_Branch)43return NULL;44/* Depending on labels->print_phase either discover local labels or45* return a name assigned with local jump target:46* - if print_phase is true and ref_value is in labels->pcs,47* return corresponding labels->name.48* - if print_phase is false, save program-local jump targets49* in labels->pcs;50*/51if (labels->print_phase) {52for (i = 0; i < labels->cnt; ++i)53if (labels->pcs[i] == ref_value)54return labels->names[i];55} else {56if (labels->cnt < MAX_LOCAL_LABELS && ref_value < labels->prog_len)57labels->pcs[labels->cnt++] = ref_value;58}59return NULL;60}6162static int disasm_insn(LLVMDisasmContextRef ctx, uint8_t *image, __u32 len, __u32 pc,63char *buf, __u32 buf_sz)64{65int i, cnt;6667cnt = LLVMDisasmInstruction(ctx, image + pc, len - pc, pc,68buf, buf_sz);69if (cnt > 0)70return cnt;71PRINT_FAIL("Can't disasm instruction at offset %d:", pc);72for (i = 0; i < 16 && pc + i < len; ++i)73printf(" %02x", image[pc + i]);74printf("\n");75return -EINVAL;76}7778static int cmp_u32(const void *_a, const void *_b)79{80__u32 a = *(__u32 *)_a;81__u32 b = *(__u32 *)_b;8283if (a < b)84return -1;85if (a > b)86return 1;87return 0;88}8990static int disasm_one_func(FILE *text_out, uint8_t *image, __u32 len)91{92char *label, *colon, *triple = NULL;93LLVMDisasmContextRef ctx = NULL;94struct local_labels labels = {};95__u32 *label_pc, pc;96int i, cnt, err = 0;97char buf[64];9899triple = LLVMGetDefaultTargetTriple();100ctx = LLVMCreateDisasm(triple, &labels, 0, NULL, lookup_symbol);101if (!ASSERT_OK_PTR(ctx, "LLVMCreateDisasm")) {102err = -EINVAL;103goto out;104}105106cnt = LLVMSetDisasmOptions(ctx, LLVMDisassembler_Option_PrintImmHex);107if (!ASSERT_EQ(cnt, 1, "LLVMSetDisasmOptions")) {108err = -EINVAL;109goto out;110}111112/* discover labels */113labels.prog_len = len;114pc = 0;115while (pc < len) {116cnt = disasm_insn(ctx, image, len, pc, buf, 1);117if (cnt < 0) {118err = cnt;119goto out;120}121pc += cnt;122}123qsort(labels.pcs, labels.cnt, sizeof(*labels.pcs), cmp_u32);124for (i = 0; i < labels.cnt; ++i)125/* gcc is unable to infer upper bound for labels.cnt and assumes126* it to be U32_MAX. U32_MAX takes 10 decimal digits.127* snprintf below prints into labels.names[*],128* which has space only for two digits and a letter.129* To avoid truncation warning use (i % MAX_LOCAL_LABELS),130* which informs gcc about printed value upper bound.131*/132snprintf(labels.names[i], sizeof(labels.names[i]), "L%d", i % MAX_LOCAL_LABELS);133134/* now print with labels */135labels.print_phase = true;136pc = 0;137while (pc < len) {138cnt = disasm_insn(ctx, image, len, pc, buf, sizeof(buf));139if (cnt < 0) {140err = cnt;141goto out;142}143label_pc = bsearch(&pc, labels.pcs, labels.cnt, sizeof(*labels.pcs), cmp_u32);144label = "";145colon = "";146if (label_pc) {147label = labels.names[label_pc - labels.pcs];148colon = ":";149}150fprintf(text_out, "%x:\t", pc);151for (i = 0; i < cnt; ++i)152fprintf(text_out, "%02x ", image[pc + i]);153for (i = cnt * 3; i < 12 * 3; ++i)154fputc(' ', text_out);155fprintf(text_out, "%s%s%s\n", label, colon, buf);156pc += cnt;157}158159out:160if (triple)161LLVMDisposeMessage(triple);162if (ctx)163LLVMDisasmDispose(ctx);164return err;165}166167int get_jited_program_text(int fd, char *text, size_t text_sz)168{169struct bpf_prog_info info = {};170__u32 info_len = sizeof(info);171__u32 jited_funcs, len, pc;172__u32 *func_lens = NULL;173FILE *text_out = NULL;174uint8_t *image = NULL;175int i, err = 0;176177if (!llvm_initialized) {178LLVMInitializeAllTargetInfos();179LLVMInitializeAllTargetMCs();180LLVMInitializeAllDisassemblers();181llvm_initialized = 1;182}183184text_out = fmemopen(text, text_sz, "w");185if (!ASSERT_OK_PTR(text_out, "open_memstream")) {186err = -errno;187goto out;188}189190/* first call is to find out jited program len */191err = bpf_prog_get_info_by_fd(fd, &info, &info_len);192if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #1"))193goto out;194195len = info.jited_prog_len;196image = malloc(len);197if (!ASSERT_OK_PTR(image, "malloc(info.jited_prog_len)")) {198err = -ENOMEM;199goto out;200}201202jited_funcs = info.nr_jited_func_lens;203func_lens = malloc(jited_funcs * sizeof(__u32));204if (!ASSERT_OK_PTR(func_lens, "malloc(info.nr_jited_func_lens)")) {205err = -ENOMEM;206goto out;207}208209memset(&info, 0, sizeof(info));210info.jited_prog_insns = (__u64)image;211info.jited_prog_len = len;212info.jited_func_lens = (__u64)func_lens;213info.nr_jited_func_lens = jited_funcs;214err = bpf_prog_get_info_by_fd(fd, &info, &info_len);215if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #2"))216goto out;217218for (pc = 0, i = 0; i < jited_funcs; ++i) {219fprintf(text_out, "func #%d:\n", i);220disasm_one_func(text_out, image + pc, func_lens[i]);221fprintf(text_out, "\n");222pc += func_lens[i];223}224225out:226if (text_out)227fclose(text_out);228if (image)229free(image);230if (func_lens)231free(func_lens);232return err;233}234235#else /* HAVE_LLVM_SUPPORT */236237int get_jited_program_text(int fd, char *text, size_t text_sz)238{239if (env.verbosity >= VERBOSE_VERY)240printf("compiled w/o llvm development libraries, can't dis-assembly binary code");241return -EOPNOTSUPP;242}243244#endif /* HAVE_LLVM_SUPPORT */245246247