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