Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
0002 /* Copyright (C) 2018 Netronome Systems, Inc. */
0003 
0004 #define _GNU_SOURCE
0005 #include <stdarg.h>
0006 #include <stdio.h>
0007 #include <stdlib.h>
0008 #include <string.h>
0009 #include <sys/types.h>
0010 #include <bpf/libbpf.h>
0011 #include <bpf/libbpf_internal.h>
0012 
0013 #include "disasm.h"
0014 #include "json_writer.h"
0015 #include "main.h"
0016 #include "xlated_dumper.h"
0017 
0018 static int kernel_syms_cmp(const void *sym_a, const void *sym_b)
0019 {
0020     return ((struct kernel_sym *)sym_a)->address -
0021            ((struct kernel_sym *)sym_b)->address;
0022 }
0023 
0024 void kernel_syms_load(struct dump_data *dd)
0025 {
0026     struct kernel_sym *sym;
0027     char buff[256];
0028     void *tmp, *address;
0029     FILE *fp;
0030 
0031     fp = fopen("/proc/kallsyms", "r");
0032     if (!fp)
0033         return;
0034 
0035     while (fgets(buff, sizeof(buff), fp)) {
0036         tmp = libbpf_reallocarray(dd->sym_mapping, dd->sym_count + 1,
0037                       sizeof(*dd->sym_mapping));
0038         if (!tmp) {
0039 out:
0040             free(dd->sym_mapping);
0041             dd->sym_mapping = NULL;
0042             fclose(fp);
0043             return;
0044         }
0045         dd->sym_mapping = tmp;
0046         sym = &dd->sym_mapping[dd->sym_count];
0047         if (sscanf(buff, "%p %*c %s", &address, sym->name) != 2)
0048             continue;
0049         sym->address = (unsigned long)address;
0050         if (!strcmp(sym->name, "__bpf_call_base")) {
0051             dd->address_call_base = sym->address;
0052             /* sysctl kernel.kptr_restrict was set */
0053             if (!sym->address)
0054                 goto out;
0055         }
0056         if (sym->address)
0057             dd->sym_count++;
0058     }
0059 
0060     fclose(fp);
0061 
0062     qsort(dd->sym_mapping, dd->sym_count,
0063           sizeof(*dd->sym_mapping), kernel_syms_cmp);
0064 }
0065 
0066 void kernel_syms_destroy(struct dump_data *dd)
0067 {
0068     free(dd->sym_mapping);
0069 }
0070 
0071 struct kernel_sym *kernel_syms_search(struct dump_data *dd,
0072                       unsigned long key)
0073 {
0074     struct kernel_sym sym = {
0075         .address = key,
0076     };
0077 
0078     return dd->sym_mapping ?
0079            bsearch(&sym, dd->sym_mapping, dd->sym_count,
0080                sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL;
0081 }
0082 
0083 static void __printf(2, 3) print_insn(void *private_data, const char *fmt, ...)
0084 {
0085     va_list args;
0086 
0087     va_start(args, fmt);
0088     vprintf(fmt, args);
0089     va_end(args);
0090 }
0091 
0092 static void __printf(2, 3)
0093 print_insn_for_graph(void *private_data, const char *fmt, ...)
0094 {
0095     char buf[64], *p;
0096     va_list args;
0097 
0098     va_start(args, fmt);
0099     vsnprintf(buf, sizeof(buf), fmt, args);
0100     va_end(args);
0101 
0102     p = buf;
0103     while (*p != '\0') {
0104         if (*p == '\n') {
0105             memmove(p + 3, p, strlen(buf) + 1 - (p - buf));
0106             /* Align each instruction dump row left. */
0107             *p++ = '\\';
0108             *p++ = 'l';
0109             /* Output multiline concatenation. */
0110             *p++ = '\\';
0111         } else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') {
0112             memmove(p + 1, p, strlen(buf) + 1 - (p - buf));
0113             /* Escape special character. */
0114             *p++ = '\\';
0115         }
0116 
0117         p++;
0118     }
0119 
0120     printf("%s", buf);
0121 }
0122 
0123 static void __printf(2, 3)
0124 print_insn_json(void *private_data, const char *fmt, ...)
0125 {
0126     unsigned int l = strlen(fmt);
0127     char chomped_fmt[l];
0128     va_list args;
0129 
0130     va_start(args, fmt);
0131     if (l > 0) {
0132         strncpy(chomped_fmt, fmt, l - 1);
0133         chomped_fmt[l - 1] = '\0';
0134     }
0135     jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
0136     va_end(args);
0137 }
0138 
0139 static const char *print_call_pcrel(struct dump_data *dd,
0140                     struct kernel_sym *sym,
0141                     unsigned long address,
0142                     const struct bpf_insn *insn)
0143 {
0144     if (!dd->nr_jited_ksyms)
0145         /* Do not show address for interpreted programs */
0146         snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
0147             "%+d", insn->off);
0148     else if (sym)
0149         snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
0150              "%+d#%s", insn->off, sym->name);
0151     else
0152         snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
0153              "%+d#0x%lx", insn->off, address);
0154     return dd->scratch_buff;
0155 }
0156 
0157 static const char *print_call_helper(struct dump_data *dd,
0158                      struct kernel_sym *sym,
0159                      unsigned long address)
0160 {
0161     if (sym)
0162         snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
0163              "%s", sym->name);
0164     else
0165         snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
0166              "0x%lx", address);
0167     return dd->scratch_buff;
0168 }
0169 
0170 static const char *print_call(void *private_data,
0171                   const struct bpf_insn *insn)
0172 {
0173     struct dump_data *dd = private_data;
0174     unsigned long address = dd->address_call_base + insn->imm;
0175     struct kernel_sym *sym;
0176 
0177     if (insn->src_reg == BPF_PSEUDO_CALL &&
0178         (__u32) insn->imm < dd->nr_jited_ksyms && dd->jited_ksyms)
0179         address = dd->jited_ksyms[insn->imm];
0180 
0181     sym = kernel_syms_search(dd, address);
0182     if (insn->src_reg == BPF_PSEUDO_CALL)
0183         return print_call_pcrel(dd, sym, address, insn);
0184     else
0185         return print_call_helper(dd, sym, address);
0186 }
0187 
0188 static const char *print_imm(void *private_data,
0189                  const struct bpf_insn *insn,
0190                  __u64 full_imm)
0191 {
0192     struct dump_data *dd = private_data;
0193 
0194     if (insn->src_reg == BPF_PSEUDO_MAP_FD)
0195         snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
0196              "map[id:%u]", insn->imm);
0197     else if (insn->src_reg == BPF_PSEUDO_MAP_VALUE)
0198         snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
0199              "map[id:%u][0]+%u", insn->imm, (insn + 1)->imm);
0200     else if (insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE)
0201         snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
0202              "map[idx:%u]+%u", insn->imm, (insn + 1)->imm);
0203     else if (insn->src_reg == BPF_PSEUDO_FUNC)
0204         snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
0205              "subprog[%+d]", insn->imm);
0206     else
0207         snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
0208              "0x%llx", (unsigned long long)full_imm);
0209     return dd->scratch_buff;
0210 }
0211 
0212 void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
0213               bool opcodes, bool linum)
0214 {
0215     const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
0216     const struct bpf_insn_cbs cbs = {
0217         .cb_print   = print_insn_json,
0218         .cb_call    = print_call,
0219         .cb_imm     = print_imm,
0220         .private_data   = dd,
0221     };
0222     struct bpf_func_info *record;
0223     struct bpf_insn *insn = buf;
0224     struct btf *btf = dd->btf;
0225     bool double_insn = false;
0226     unsigned int nr_skip = 0;
0227     char func_sig[1024];
0228     unsigned int i;
0229 
0230     jsonw_start_array(json_wtr);
0231     record = dd->func_info;
0232     for (i = 0; i < len / sizeof(*insn); i++) {
0233         if (double_insn) {
0234             double_insn = false;
0235             continue;
0236         }
0237         double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
0238 
0239         jsonw_start_object(json_wtr);
0240 
0241         if (btf && record) {
0242             if (record->insn_off == i) {
0243                 btf_dumper_type_only(btf, record->type_id,
0244                              func_sig,
0245                              sizeof(func_sig));
0246                 if (func_sig[0] != '\0') {
0247                     jsonw_name(json_wtr, "proto");
0248                     jsonw_string(json_wtr, func_sig);
0249                 }
0250                 record = (void *)record + dd->finfo_rec_size;
0251             }
0252         }
0253 
0254         if (prog_linfo) {
0255             const struct bpf_line_info *linfo;
0256 
0257             linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
0258             if (linfo) {
0259                 btf_dump_linfo_json(btf, linfo, linum);
0260                 nr_skip++;
0261             }
0262         }
0263 
0264         jsonw_name(json_wtr, "disasm");
0265         print_bpf_insn(&cbs, insn + i, true);
0266 
0267         if (opcodes) {
0268             jsonw_name(json_wtr, "opcodes");
0269             jsonw_start_object(json_wtr);
0270 
0271             jsonw_name(json_wtr, "code");
0272             jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
0273 
0274             jsonw_name(json_wtr, "src_reg");
0275             jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
0276 
0277             jsonw_name(json_wtr, "dst_reg");
0278             jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
0279 
0280             jsonw_name(json_wtr, "off");
0281             print_hex_data_json((uint8_t *)(&insn[i].off), 2);
0282 
0283             jsonw_name(json_wtr, "imm");
0284             if (double_insn && i < len - 1)
0285                 print_hex_data_json((uint8_t *)(&insn[i].imm),
0286                             12);
0287             else
0288                 print_hex_data_json((uint8_t *)(&insn[i].imm),
0289                             4);
0290             jsonw_end_object(json_wtr);
0291         }
0292         jsonw_end_object(json_wtr);
0293     }
0294     jsonw_end_array(json_wtr);
0295 }
0296 
0297 void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
0298                bool opcodes, bool linum)
0299 {
0300     const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
0301     const struct bpf_insn_cbs cbs = {
0302         .cb_print   = print_insn,
0303         .cb_call    = print_call,
0304         .cb_imm     = print_imm,
0305         .private_data   = dd,
0306     };
0307     struct bpf_func_info *record;
0308     struct bpf_insn *insn = buf;
0309     struct btf *btf = dd->btf;
0310     unsigned int nr_skip = 0;
0311     bool double_insn = false;
0312     char func_sig[1024];
0313     unsigned int i;
0314 
0315     record = dd->func_info;
0316     for (i = 0; i < len / sizeof(*insn); i++) {
0317         if (double_insn) {
0318             double_insn = false;
0319             continue;
0320         }
0321 
0322         if (btf && record) {
0323             if (record->insn_off == i) {
0324                 btf_dumper_type_only(btf, record->type_id,
0325                              func_sig,
0326                              sizeof(func_sig));
0327                 if (func_sig[0] != '\0')
0328                     printf("%s:\n", func_sig);
0329                 record = (void *)record + dd->finfo_rec_size;
0330             }
0331         }
0332 
0333         if (prog_linfo) {
0334             const struct bpf_line_info *linfo;
0335 
0336             linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
0337             if (linfo) {
0338                 btf_dump_linfo_plain(btf, linfo, "; ",
0339                              linum);
0340                 nr_skip++;
0341             }
0342         }
0343 
0344         double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
0345 
0346         printf("% 4d: ", i);
0347         print_bpf_insn(&cbs, insn + i, true);
0348 
0349         if (opcodes) {
0350             printf("       ");
0351             fprint_hex(stdout, insn + i, 8, " ");
0352             if (double_insn && i < len - 1) {
0353                 printf(" ");
0354                 fprint_hex(stdout, insn + i + 1, 8, " ");
0355             }
0356             printf("\n");
0357         }
0358     }
0359 }
0360 
0361 void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
0362                unsigned int start_idx)
0363 {
0364     const struct bpf_insn_cbs cbs = {
0365         .cb_print   = print_insn_for_graph,
0366         .cb_call    = print_call,
0367         .cb_imm     = print_imm,
0368         .private_data   = dd,
0369     };
0370     struct bpf_insn *insn_start = buf_start;
0371     struct bpf_insn *insn_end = buf_end;
0372     struct bpf_insn *cur = insn_start;
0373 
0374     for (; cur <= insn_end; cur++) {
0375         printf("% 4d: ", (int)(cur - insn_start + start_idx));
0376         print_bpf_insn(&cbs, cur, true);
0377         if (cur != insn_end)
0378             printf(" | ");
0379     }
0380 }