0001
0002
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
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
0107 *p++ = '\\';
0108 *p++ = 'l';
0109
0110 *p++ = '\\';
0111 } else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') {
0112 memmove(p + 1, p, strlen(buf) + 1 - (p - buf));
0113
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
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 }