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
 200                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 201                         "0x%llx", (unsigned long long)full_imm);
 202        return dd->scratch_buff;
 203}
 204
 205void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
 206                      bool opcodes, bool linum)
 207{
 208        const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
 209        const struct bpf_insn_cbs cbs = {
 210                .cb_print       = print_insn_json,
 211                .cb_call        = print_call,
 212                .cb_imm         = print_imm,
 213                .private_data   = dd,
 214        };
 215        struct bpf_func_info *record;
 216        struct bpf_insn *insn = buf;
 217        struct btf *btf = dd->btf;
 218        bool double_insn = false;
 219        unsigned int nr_skip = 0;
 220        char func_sig[1024];
 221        unsigned int i;
 222
 223        jsonw_start_array(json_wtr);
 224        record = dd->func_info;
 225        for (i = 0; i < len / sizeof(*insn); i++) {
 226                if (double_insn) {
 227                        double_insn = false;
 228                        continue;
 229                }
 230                double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
 231
 232                jsonw_start_object(json_wtr);
 233
 234                if (btf && record) {
 235                        if (record->insn_off == i) {
 236                                btf_dumper_type_only(btf, record->type_id,
 237                                                     func_sig,
 238                                                     sizeof(func_sig));
 239                                if (func_sig[0] != '\0') {
 240                                        jsonw_name(json_wtr, "proto");
 241                                        jsonw_string(json_wtr, func_sig);
 242                                }
 243                                record = (void *)record + dd->finfo_rec_size;
 244                        }
 245                }
 246
 247                if (prog_linfo) {
 248                        const struct bpf_line_info *linfo;
 249
 250                        linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
 251                        if (linfo) {
 252                                btf_dump_linfo_json(btf, linfo, linum);
 253                                nr_skip++;
 254                        }
 255                }
 256
 257                jsonw_name(json_wtr, "disasm");
 258                print_bpf_insn(&cbs, insn + i, true);
 259
 260                if (opcodes) {
 261                        jsonw_name(json_wtr, "opcodes");
 262                        jsonw_start_object(json_wtr);
 263
 264                        jsonw_name(json_wtr, "code");
 265                        jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
 266
 267                        jsonw_name(json_wtr, "src_reg");
 268                        jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
 269
 270                        jsonw_name(json_wtr, "dst_reg");
 271                        jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
 272
 273                        jsonw_name(json_wtr, "off");
 274                        print_hex_data_json((uint8_t *)(&insn[i].off), 2);
 275
 276                        jsonw_name(json_wtr, "imm");
 277                        if (double_insn && i < len - 1)
 278                                print_hex_data_json((uint8_t *)(&insn[i].imm),
 279                                                    12);
 280                        else
 281                                print_hex_data_json((uint8_t *)(&insn[i].imm),
 282                                                    4);
 283                        jsonw_end_object(json_wtr);
 284                }
 285                jsonw_end_object(json_wtr);
 286        }
 287        jsonw_end_array(json_wtr);
 288}
 289
 290void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
 291                       bool opcodes, bool linum)
 292{
 293        const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
 294        const struct bpf_insn_cbs cbs = {
 295                .cb_print       = print_insn,
 296                .cb_call        = print_call,
 297                .cb_imm         = print_imm,
 298                .private_data   = dd,
 299        };
 300        struct bpf_func_info *record;
 301        struct bpf_insn *insn = buf;
 302        struct btf *btf = dd->btf;
 303        unsigned int nr_skip = 0;
 304        bool double_insn = false;
 305        char func_sig[1024];
 306        unsigned int i;
 307
 308        record = dd->func_info;
 309        for (i = 0; i < len / sizeof(*insn); i++) {
 310                if (double_insn) {
 311                        double_insn = false;
 312                        continue;
 313                }
 314
 315                if (btf && record) {
 316                        if (record->insn_off == i) {
 317                                btf_dumper_type_only(btf, record->type_id,
 318                                                     func_sig,
 319                                                     sizeof(func_sig));
 320                                if (func_sig[0] != '\0')
 321                                        printf("%s:\n", func_sig);
 322                                record = (void *)record + dd->finfo_rec_size;
 323                        }
 324                }
 325
 326                if (prog_linfo) {
 327                        const struct bpf_line_info *linfo;
 328
 329                        linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
 330                        if (linfo) {
 331                                btf_dump_linfo_plain(btf, linfo, "; ",
 332                                                     linum);
 333                                nr_skip++;
 334                        }
 335                }
 336
 337                double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
 338
 339                printf("% 4d: ", i);
 340                print_bpf_insn(&cbs, insn + i, true);
 341
 342                if (opcodes) {
 343                        printf("       ");
 344                        fprint_hex(stdout, insn + i, 8, " ");
 345                        if (double_insn && i < len - 1) {
 346                                printf(" ");
 347                                fprint_hex(stdout, insn + i + 1, 8, " ");
 348                        }
 349                        printf("\n");
 350                }
 351        }
 352}
 353
 354void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
 355                           unsigned int start_idx)
 356{
 357        const struct bpf_insn_cbs cbs = {
 358                .cb_print       = print_insn_for_graph,
 359                .cb_call        = print_call,
 360                .cb_imm         = print_imm,
 361                .private_data   = dd,
 362        };
 363        struct bpf_insn *insn_start = buf_start;
 364        struct bpf_insn *insn_end = buf_end;
 365        struct bpf_insn *cur = insn_start;
 366
 367        for (; cur <= insn_end; cur++) {
 368                printf("% 4d: ", (int)(cur - insn_start + start_idx));
 369                print_bpf_insn(&cbs, cur, true);
 370                if (cur != insn_end)
 371                        printf(" | ");
 372        }
 373}
 374