linux/tools/bpf/bpftool/xlated_dumper.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
   2/* Copyright (C) 2018 Netronome Systems, Inc. */
   3
   4#define _GNU_SOURCE
   5#include <stdarg.h>
   6#include <stdio.h>
   7#include <stdlib.h>
   8#include <string.h>
   9#include <sys/types.h>
  10#include <bpf/libbpf.h>
  11
  12#include "disasm.h"
  13#include "json_writer.h"
  14#include "main.h"
  15#include "xlated_dumper.h"
  16
  17static int kernel_syms_cmp(const void *sym_a, const void *sym_b)
  18{
  19        return ((struct kernel_sym *)sym_a)->address -
  20               ((struct kernel_sym *)sym_b)->address;
  21}
  22
  23void kernel_syms_load(struct dump_data *dd)
  24{
  25        struct kernel_sym *sym;
  26        char buff[256];
  27        void *tmp, *address;
  28        FILE *fp;
  29
  30        fp = fopen("/proc/kallsyms", "r");
  31        if (!fp)
  32                return;
  33
  34        while (fgets(buff, sizeof(buff), fp)) {
  35                tmp = reallocarray(dd->sym_mapping, dd->sym_count + 1,
  36                                   sizeof(*dd->sym_mapping));
  37                if (!tmp) {
  38out:
  39                        free(dd->sym_mapping);
  40                        dd->sym_mapping = NULL;
  41                        fclose(fp);
  42                        return;
  43                }
  44                dd->sym_mapping = tmp;
  45                sym = &dd->sym_mapping[dd->sym_count];
  46                if (sscanf(buff, "%p %*c %s", &address, sym->name) != 2)
  47                        continue;
  48                sym->address = (unsigned long)address;
  49                if (!strcmp(sym->name, "__bpf_call_base")) {
  50                        dd->address_call_base = sym->address;
  51                        /* sysctl kernel.kptr_restrict was set */
  52                        if (!sym->address)
  53                                goto out;
  54                }
  55                if (sym->address)
  56                        dd->sym_count++;
  57        }
  58
  59        fclose(fp);
  60
  61        qsort(dd->sym_mapping, dd->sym_count,
  62              sizeof(*dd->sym_mapping), kernel_syms_cmp);
  63}
  64
  65void kernel_syms_destroy(struct dump_data *dd)
  66{
  67        free(dd->sym_mapping);
  68}
  69
  70struct kernel_sym *kernel_syms_search(struct dump_data *dd,
  71                                      unsigned long key)
  72{
  73        struct kernel_sym sym = {
  74                .address = key,
  75        };
  76
  77        return dd->sym_mapping ?
  78               bsearch(&sym, dd->sym_mapping, dd->sym_count,
  79                       sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL;
  80}
  81
  82static void __printf(2, 3) print_insn(void *private_data, const char *fmt, ...)
  83{
  84        va_list args;
  85
  86        va_start(args, fmt);
  87        vprintf(fmt, args);
  88        va_end(args);
  89}
  90
  91static void __printf(2, 3)
  92print_insn_for_graph(void *private_data, const char *fmt, ...)
  93{
  94        char buf[64], *p;
  95        va_list args;
  96
  97        va_start(args, fmt);
  98        vsnprintf(buf, sizeof(buf), fmt, args);
  99        va_end(args);
 100
 101        p = buf;
 102        while (*p != '\0') {
 103                if (*p == '\n') {
 104                        memmove(p + 3, p, strlen(buf) + 1 - (p - buf));
 105                        /* Align each instruction dump row left. */
 106                        *p++ = '\\';
 107                        *p++ = 'l';
 108                        /* Output multiline concatenation. */
 109                        *p++ = '\\';
 110                } else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') {
 111                        memmove(p + 1, p, strlen(buf) + 1 - (p - buf));
 112                        /* Escape special character. */
 113                        *p++ = '\\';
 114                }
 115
 116                p++;
 117        }
 118
 119        printf("%s", buf);
 120}
 121
 122static void __printf(2, 3)
 123print_insn_json(void *private_data, const char *fmt, ...)
 124{
 125        unsigned int l = strlen(fmt);
 126        char chomped_fmt[l];
 127        va_list args;
 128
 129        va_start(args, fmt);
 130        if (l > 0) {
 131                strncpy(chomped_fmt, fmt, l - 1);
 132                chomped_fmt[l - 1] = '\0';
 133        }
 134        jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
 135        va_end(args);
 136}
 137
 138static const char *print_call_pcrel(struct dump_data *dd,
 139                                    struct kernel_sym *sym,
 140                                    unsigned long address,
 141                                    const struct bpf_insn *insn)
 142{
 143        if (!dd->nr_jited_ksyms)
 144                /* Do not show address for interpreted programs */
 145                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 146                        "%+d", insn->off);
 147        else if (sym)
 148                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 149                         "%+d#%s", insn->off, sym->name);
 150        else
 151                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 152                         "%+d#0x%lx", insn->off, address);
 153        return dd->scratch_buff;
 154}
 155
 156static const char *print_call_helper(struct dump_data *dd,
 157                                     struct kernel_sym *sym,
 158                                     unsigned long address)
 159{
 160        if (sym)
 161                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 162                         "%s", sym->name);
 163        else
 164                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 165                         "0x%lx", address);
 166        return dd->scratch_buff;
 167}
 168
 169static const char *print_call(void *private_data,
 170                              const struct bpf_insn *insn)
 171{
 172        struct dump_data *dd = private_data;
 173        unsigned long address = dd->address_call_base + insn->imm;
 174        struct kernel_sym *sym;
 175
 176        if (insn->src_reg == BPF_PSEUDO_CALL &&
 177            (__u32) insn->imm < dd->nr_jited_ksyms && dd->jited_ksyms)
 178                address = dd->jited_ksyms[insn->imm];
 179
 180        sym = kernel_syms_search(dd, address);
 181        if (insn->src_reg == BPF_PSEUDO_CALL)
 182                return print_call_pcrel(dd, sym, address, insn);
 183        else
 184                return print_call_helper(dd, sym, address);
 185}
 186
 187static const char *print_imm(void *private_data,
 188                             const struct bpf_insn *insn,
 189                             __u64 full_imm)
 190{
 191        struct dump_data *dd = private_data;
 192
 193        if (insn->src_reg == BPF_PSEUDO_MAP_FD)
 194                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 195                         "map[id:%u]", insn->imm);
 196        else if (insn->src_reg == BPF_PSEUDO_MAP_VALUE)
 197                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 198                         "map[id:%u][0]+%u", insn->imm, (insn + 1)->imm);
 199        else if (insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE)
 200                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 201                         "map[idx:%u]+%u", insn->imm, (insn + 1)->imm);
 202        else if (insn->src_reg == BPF_PSEUDO_FUNC)
 203                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 204                         "subprog[%+d]", insn->imm);
 205        else
 206                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 207                         "0x%llx", (unsigned long long)full_imm);
 208        return dd->scratch_buff;
 209}
 210
 211void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
 212                      bool opcodes, bool linum)
 213{
 214        const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
 215        const struct bpf_insn_cbs cbs = {
 216                .cb_print       = print_insn_json,
 217                .cb_call        = print_call,
 218                .cb_imm         = print_imm,
 219                .private_data   = dd,
 220        };
 221        struct bpf_func_info *record;
 222        struct bpf_insn *insn = buf;
 223        struct btf *btf = dd->btf;
 224        bool double_insn = false;
 225        unsigned int nr_skip = 0;
 226        char func_sig[1024];
 227        unsigned int i;
 228
 229        jsonw_start_array(json_wtr);
 230        record = dd->func_info;
 231        for (i = 0; i < len / sizeof(*insn); i++) {
 232                if (double_insn) {
 233                        double_insn = false;
 234                        continue;
 235                }
 236                double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
 237
 238                jsonw_start_object(json_wtr);
 239
 240                if (btf && record) {
 241                        if (record->insn_off == i) {
 242                                btf_dumper_type_only(btf, record->type_id,
 243                                                     func_sig,
 244                                                     sizeof(func_sig));
 245                                if (func_sig[0] != '\0') {
 246                                        jsonw_name(json_wtr, "proto");
 247                                        jsonw_string(json_wtr, func_sig);
 248                                }
 249                                record = (void *)record + dd->finfo_rec_size;
 250                        }
 251                }
 252
 253                if (prog_linfo) {
 254                        const struct bpf_line_info *linfo;
 255
 256                        linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
 257                        if (linfo) {
 258                                btf_dump_linfo_json(btf, linfo, linum);
 259                                nr_skip++;
 260                        }
 261                }
 262
 263                jsonw_name(json_wtr, "disasm");
 264                print_bpf_insn(&cbs, insn + i, true);
 265
 266                if (opcodes) {
 267                        jsonw_name(json_wtr, "opcodes");
 268                        jsonw_start_object(json_wtr);
 269
 270                        jsonw_name(json_wtr, "code");
 271                        jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
 272
 273                        jsonw_name(json_wtr, "src_reg");
 274                        jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
 275
 276                        jsonw_name(json_wtr, "dst_reg");
 277                        jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
 278
 279                        jsonw_name(json_wtr, "off");
 280                        print_hex_data_json((uint8_t *)(&insn[i].off), 2);
 281
 282                        jsonw_name(json_wtr, "imm");
 283                        if (double_insn && i < len - 1)
 284                                print_hex_data_json((uint8_t *)(&insn[i].imm),
 285                                                    12);
 286                        else
 287                                print_hex_data_json((uint8_t *)(&insn[i].imm),
 288                                                    4);
 289                        jsonw_end_object(json_wtr);
 290                }
 291                jsonw_end_object(json_wtr);
 292        }
 293        jsonw_end_array(json_wtr);
 294}
 295
 296void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
 297                       bool opcodes, bool linum)
 298{
 299        const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
 300        const struct bpf_insn_cbs cbs = {
 301                .cb_print       = print_insn,
 302                .cb_call        = print_call,
 303                .cb_imm         = print_imm,
 304                .private_data   = dd,
 305        };
 306        struct bpf_func_info *record;
 307        struct bpf_insn *insn = buf;
 308        struct btf *btf = dd->btf;
 309        unsigned int nr_skip = 0;
 310        bool double_insn = false;
 311        char func_sig[1024];
 312        unsigned int i;
 313
 314        record = dd->func_info;
 315        for (i = 0; i < len / sizeof(*insn); i++) {
 316                if (double_insn) {
 317                        double_insn = false;
 318                        continue;
 319                }
 320
 321                if (btf && record) {
 322                        if (record->insn_off == i) {
 323                                btf_dumper_type_only(btf, record->type_id,
 324                                                     func_sig,
 325                                                     sizeof(func_sig));
 326                                if (func_sig[0] != '\0')
 327                                        printf("%s:\n", func_sig);
 328                                record = (void *)record + dd->finfo_rec_size;
 329                        }
 330                }
 331
 332                if (prog_linfo) {
 333                        const struct bpf_line_info *linfo;
 334
 335                        linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
 336                        if (linfo) {
 337                                btf_dump_linfo_plain(btf, linfo, "; ",
 338                                                     linum);
 339                                nr_skip++;
 340                        }
 341                }
 342
 343                double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
 344
 345                printf("% 4d: ", i);
 346                print_bpf_insn(&cbs, insn + i, true);
 347
 348                if (opcodes) {
 349                        printf("       ");
 350                        fprint_hex(stdout, insn + i, 8, " ");
 351                        if (double_insn && i < len - 1) {
 352                                printf(" ");
 353                                fprint_hex(stdout, insn + i + 1, 8, " ");
 354                        }
 355                        printf("\n");
 356                }
 357        }
 358}
 359
 360void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
 361                           unsigned int start_idx)
 362{
 363        const struct bpf_insn_cbs cbs = {
 364                .cb_print       = print_insn_for_graph,
 365                .cb_call        = print_call,
 366                .cb_imm         = print_imm,
 367                .private_data   = dd,
 368        };
 369        struct bpf_insn *insn_start = buf_start;
 370        struct bpf_insn *insn_end = buf_end;
 371        struct bpf_insn *cur = insn_start;
 372
 373        for (; cur <= insn_end; cur++) {
 374                printf("% 4d: ", (int)(cur - insn_start + start_idx));
 375                print_bpf_insn(&cbs, cur, true);
 376                if (cur != insn_end)
 377                        printf(" | ");
 378        }
 379}
 380