| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
- #include <bpf/bpf.h>
- #include <bpf/libbpf.h>
- #include <test_progs.h>
- #ifdef HAVE_LLVM_SUPPORT
- #include <llvm-c/Core.h>
- #include <llvm-c/Disassembler.h>
- #include <llvm-c/Target.h>
- #include <llvm-c/TargetMachine.h>
- /* The intent is to use get_jited_program_text() for small test
- * programs written in BPF assembly, thus assume that 32 local labels
- * would be sufficient.
- */
- #define MAX_LOCAL_LABELS 32
- /* Local labels are encoded as 'L42', this requires 4 bytes of storage:
- * 3 characters + zero byte
- */
- #define LOCAL_LABEL_LEN 4
- static bool llvm_initialized;
- struct local_labels {
- bool print_phase;
- __u32 prog_len;
- __u32 cnt;
- __u32 pcs[MAX_LOCAL_LABELS];
- char names[MAX_LOCAL_LABELS][LOCAL_LABEL_LEN];
- };
- static const char *lookup_symbol(void *data, uint64_t ref_value, uint64_t *ref_type,
- uint64_t ref_pc, const char **ref_name)
- {
- struct local_labels *labels = data;
- uint64_t type = *ref_type;
- int i;
- *ref_type = LLVMDisassembler_ReferenceType_InOut_None;
- *ref_name = NULL;
- if (type != LLVMDisassembler_ReferenceType_In_Branch)
- return NULL;
- /* Depending on labels->print_phase either discover local labels or
- * return a name assigned with local jump target:
- * - if print_phase is true and ref_value is in labels->pcs,
- * return corresponding labels->name.
- * - if print_phase is false, save program-local jump targets
- * in labels->pcs;
- */
- if (labels->print_phase) {
- for (i = 0; i < labels->cnt; ++i)
- if (labels->pcs[i] == ref_value)
- return labels->names[i];
- } else {
- if (labels->cnt < MAX_LOCAL_LABELS && ref_value < labels->prog_len)
- labels->pcs[labels->cnt++] = ref_value;
- }
- return NULL;
- }
- static int disasm_insn(LLVMDisasmContextRef ctx, uint8_t *image, __u32 len, __u32 pc,
- char *buf, __u32 buf_sz)
- {
- int i, cnt;
- cnt = LLVMDisasmInstruction(ctx, image + pc, len - pc, pc,
- buf, buf_sz);
- if (cnt > 0)
- return cnt;
- PRINT_FAIL("Can't disasm instruction at offset %d:", pc);
- for (i = 0; i < 16 && pc + i < len; ++i)
- printf(" %02x", image[pc + i]);
- printf("\n");
- return -EINVAL;
- }
- static int cmp_u32(const void *_a, const void *_b)
- {
- __u32 a = *(__u32 *)_a;
- __u32 b = *(__u32 *)_b;
- if (a < b)
- return -1;
- if (a > b)
- return 1;
- return 0;
- }
- static int disasm_one_func(FILE *text_out, uint8_t *image, __u32 len)
- {
- char *label, *colon, *triple = NULL;
- LLVMDisasmContextRef ctx = NULL;
- struct local_labels labels = {};
- __u32 *label_pc, pc;
- int i, cnt, err = 0;
- char buf[64];
- triple = LLVMGetDefaultTargetTriple();
- ctx = LLVMCreateDisasm(triple, &labels, 0, NULL, lookup_symbol);
- if (!ASSERT_OK_PTR(ctx, "LLVMCreateDisasm")) {
- err = -EINVAL;
- goto out;
- }
- cnt = LLVMSetDisasmOptions(ctx, LLVMDisassembler_Option_PrintImmHex);
- if (!ASSERT_EQ(cnt, 1, "LLVMSetDisasmOptions")) {
- err = -EINVAL;
- goto out;
- }
- /* discover labels */
- labels.prog_len = len;
- pc = 0;
- while (pc < len) {
- cnt = disasm_insn(ctx, image, len, pc, buf, 1);
- if (cnt < 0) {
- err = cnt;
- goto out;
- }
- pc += cnt;
- }
- qsort(labels.pcs, labels.cnt, sizeof(*labels.pcs), cmp_u32);
- for (i = 0; i < labels.cnt; ++i)
- /* gcc is unable to infer upper bound for labels.cnt and assumes
- * it to be U32_MAX. U32_MAX takes 10 decimal digits.
- * snprintf below prints into labels.names[*],
- * which has space only for two digits and a letter.
- * To avoid truncation warning use (i % MAX_LOCAL_LABELS),
- * which informs gcc about printed value upper bound.
- */
- snprintf(labels.names[i], sizeof(labels.names[i]), "L%d", i % MAX_LOCAL_LABELS);
- /* now print with labels */
- labels.print_phase = true;
- pc = 0;
- while (pc < len) {
- cnt = disasm_insn(ctx, image, len, pc, buf, sizeof(buf));
- if (cnt < 0) {
- err = cnt;
- goto out;
- }
- label_pc = bsearch(&pc, labels.pcs, labels.cnt, sizeof(*labels.pcs), cmp_u32);
- label = "";
- colon = "";
- if (label_pc) {
- label = labels.names[label_pc - labels.pcs];
- colon = ":";
- }
- fprintf(text_out, "%x:\t", pc);
- for (i = 0; i < cnt; ++i)
- fprintf(text_out, "%02x ", image[pc + i]);
- for (i = cnt * 3; i < 12 * 3; ++i)
- fputc(' ', text_out);
- fprintf(text_out, "%s%s%s\n", label, colon, buf);
- pc += cnt;
- }
- out:
- if (triple)
- LLVMDisposeMessage(triple);
- if (ctx)
- LLVMDisasmDispose(ctx);
- return err;
- }
- int get_jited_program_text(int fd, char *text, size_t text_sz)
- {
- struct bpf_prog_info info = {};
- __u32 info_len = sizeof(info);
- __u32 jited_funcs, len, pc;
- __u32 *func_lens = NULL;
- FILE *text_out = NULL;
- uint8_t *image = NULL;
- int i, err = 0;
- if (!llvm_initialized) {
- LLVMInitializeAllTargetInfos();
- LLVMInitializeAllTargetMCs();
- LLVMInitializeAllDisassemblers();
- llvm_initialized = 1;
- }
- text_out = fmemopen(text, text_sz, "w");
- if (!ASSERT_OK_PTR(text_out, "open_memstream")) {
- err = -errno;
- goto out;
- }
- /* first call is to find out jited program len */
- err = bpf_prog_get_info_by_fd(fd, &info, &info_len);
- if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #1"))
- goto out;
- len = info.jited_prog_len;
- image = malloc(len);
- if (!ASSERT_OK_PTR(image, "malloc(info.jited_prog_len)")) {
- err = -ENOMEM;
- goto out;
- }
- jited_funcs = info.nr_jited_func_lens;
- func_lens = malloc(jited_funcs * sizeof(__u32));
- if (!ASSERT_OK_PTR(func_lens, "malloc(info.nr_jited_func_lens)")) {
- err = -ENOMEM;
- goto out;
- }
- memset(&info, 0, sizeof(info));
- info.jited_prog_insns = (__u64)image;
- info.jited_prog_len = len;
- info.jited_func_lens = (__u64)func_lens;
- info.nr_jited_func_lens = jited_funcs;
- err = bpf_prog_get_info_by_fd(fd, &info, &info_len);
- if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #2"))
- goto out;
- for (pc = 0, i = 0; i < jited_funcs; ++i) {
- fprintf(text_out, "func #%d:\n", i);
- disasm_one_func(text_out, image + pc, func_lens[i]);
- fprintf(text_out, "\n");
- pc += func_lens[i];
- }
- out:
- if (text_out)
- fclose(text_out);
- if (image)
- free(image);
- if (func_lens)
- free(func_lens);
- return err;
- }
- #else /* HAVE_LLVM_SUPPORT */
- int get_jited_program_text(int fd, char *text, size_t text_sz)
- {
- if (env.verbosity >= VERBOSE_VERY)
- printf("compiled w/o llvm development libraries, can't dis-assembly binary code");
- return -EOPNOTSUPP;
- }
- #endif /* HAVE_LLVM_SUPPORT */
|