linux/tools/bpf/bpftool/prog.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2017 Netronome Systems, Inc.
   3 *
   4 * This software is dual licensed under the GNU General License Version 2,
   5 * June 1991 as shown in the file COPYING in the top-level directory of this
   6 * source tree or the BSD 2-Clause License provided below.  You have the
   7 * option to license this software under the complete terms of either license.
   8 *
   9 * The BSD 2-Clause License:
  10 *
  11 *     Redistribution and use in source and binary forms, with or
  12 *     without modification, are permitted provided that the following
  13 *     conditions are met:
  14 *
  15 *      1. Redistributions of source code must retain the above
  16 *         copyright notice, this list of conditions and the following
  17 *         disclaimer.
  18 *
  19 *      2. Redistributions in binary form must reproduce the above
  20 *         copyright notice, this list of conditions and the following
  21 *         disclaimer in the documentation and/or other materials
  22 *         provided with the distribution.
  23 *
  24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31 * SOFTWARE.
  32 */
  33
  34/* Author: Jakub Kicinski <kubakici@wp.pl> */
  35
  36#include <errno.h>
  37#include <fcntl.h>
  38#include <stdarg.h>
  39#include <stdio.h>
  40#include <stdlib.h>
  41#include <string.h>
  42#include <time.h>
  43#include <unistd.h>
  44#include <sys/types.h>
  45#include <sys/stat.h>
  46
  47#include <bpf.h>
  48#include <libbpf.h>
  49
  50#include "main.h"
  51#include "disasm.h"
  52
  53static const char * const prog_type_name[] = {
  54        [BPF_PROG_TYPE_UNSPEC]          = "unspec",
  55        [BPF_PROG_TYPE_SOCKET_FILTER]   = "socket_filter",
  56        [BPF_PROG_TYPE_KPROBE]          = "kprobe",
  57        [BPF_PROG_TYPE_SCHED_CLS]       = "sched_cls",
  58        [BPF_PROG_TYPE_SCHED_ACT]       = "sched_act",
  59        [BPF_PROG_TYPE_TRACEPOINT]      = "tracepoint",
  60        [BPF_PROG_TYPE_XDP]             = "xdp",
  61        [BPF_PROG_TYPE_PERF_EVENT]      = "perf_event",
  62        [BPF_PROG_TYPE_CGROUP_SKB]      = "cgroup_skb",
  63        [BPF_PROG_TYPE_CGROUP_SOCK]     = "cgroup_sock",
  64        [BPF_PROG_TYPE_LWT_IN]          = "lwt_in",
  65        [BPF_PROG_TYPE_LWT_OUT]         = "lwt_out",
  66        [BPF_PROG_TYPE_LWT_XMIT]        = "lwt_xmit",
  67        [BPF_PROG_TYPE_SOCK_OPS]        = "sock_ops",
  68        [BPF_PROG_TYPE_SK_SKB]          = "sk_skb",
  69        [BPF_PROG_TYPE_CGROUP_DEVICE]   = "cgroup_device",
  70};
  71
  72static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
  73{
  74        struct timespec real_time_ts, boot_time_ts;
  75        time_t wallclock_secs;
  76        struct tm load_tm;
  77
  78        buf[--size] = '\0';
  79
  80        if (clock_gettime(CLOCK_REALTIME, &real_time_ts) ||
  81            clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) {
  82                perror("Can't read clocks");
  83                snprintf(buf, size, "%llu", nsecs / 1000000000);
  84                return;
  85        }
  86
  87        wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) +
  88                nsecs / 1000000000;
  89
  90        if (!localtime_r(&wallclock_secs, &load_tm)) {
  91                snprintf(buf, size, "%llu", nsecs / 1000000000);
  92                return;
  93        }
  94
  95        strftime(buf, size, "%b %d/%H:%M", &load_tm);
  96}
  97
  98static int prog_fd_by_tag(unsigned char *tag)
  99{
 100        struct bpf_prog_info info = {};
 101        __u32 len = sizeof(info);
 102        unsigned int id = 0;
 103        int err;
 104        int fd;
 105
 106        while (true) {
 107                err = bpf_prog_get_next_id(id, &id);
 108                if (err) {
 109                        p_err("%s", strerror(errno));
 110                        return -1;
 111                }
 112
 113                fd = bpf_prog_get_fd_by_id(id);
 114                if (fd < 0) {
 115                        p_err("can't get prog by id (%u): %s",
 116                              id, strerror(errno));
 117                        return -1;
 118                }
 119
 120                err = bpf_obj_get_info_by_fd(fd, &info, &len);
 121                if (err) {
 122                        p_err("can't get prog info (%u): %s",
 123                              id, strerror(errno));
 124                        close(fd);
 125                        return -1;
 126                }
 127
 128                if (!memcmp(tag, info.tag, BPF_TAG_SIZE))
 129                        return fd;
 130
 131                close(fd);
 132        }
 133}
 134
 135int prog_parse_fd(int *argc, char ***argv)
 136{
 137        int fd;
 138
 139        if (is_prefix(**argv, "id")) {
 140                unsigned int id;
 141                char *endptr;
 142
 143                NEXT_ARGP();
 144
 145                id = strtoul(**argv, &endptr, 0);
 146                if (*endptr) {
 147                        p_err("can't parse %s as ID", **argv);
 148                        return -1;
 149                }
 150                NEXT_ARGP();
 151
 152                fd = bpf_prog_get_fd_by_id(id);
 153                if (fd < 0)
 154                        p_err("get by id (%u): %s", id, strerror(errno));
 155                return fd;
 156        } else if (is_prefix(**argv, "tag")) {
 157                unsigned char tag[BPF_TAG_SIZE];
 158
 159                NEXT_ARGP();
 160
 161                if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2,
 162                           tag + 3, tag + 4, tag + 5, tag + 6, tag + 7)
 163                    != BPF_TAG_SIZE) {
 164                        p_err("can't parse tag");
 165                        return -1;
 166                }
 167                NEXT_ARGP();
 168
 169                return prog_fd_by_tag(tag);
 170        } else if (is_prefix(**argv, "pinned")) {
 171                char *path;
 172
 173                NEXT_ARGP();
 174
 175                path = **argv;
 176                NEXT_ARGP();
 177
 178                return open_obj_pinned_any(path, BPF_OBJ_PROG);
 179        }
 180
 181        p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv);
 182        return -1;
 183}
 184
 185static void show_prog_maps(int fd, u32 num_maps)
 186{
 187        struct bpf_prog_info info = {};
 188        __u32 len = sizeof(info);
 189        __u32 map_ids[num_maps];
 190        unsigned int i;
 191        int err;
 192
 193        info.nr_map_ids = num_maps;
 194        info.map_ids = ptr_to_u64(map_ids);
 195
 196        err = bpf_obj_get_info_by_fd(fd, &info, &len);
 197        if (err || !info.nr_map_ids)
 198                return;
 199
 200        if (json_output) {
 201                jsonw_name(json_wtr, "map_ids");
 202                jsonw_start_array(json_wtr);
 203                for (i = 0; i < info.nr_map_ids; i++)
 204                        jsonw_uint(json_wtr, map_ids[i]);
 205                jsonw_end_array(json_wtr);
 206        } else {
 207                printf("  map_ids ");
 208                for (i = 0; i < info.nr_map_ids; i++)
 209                        printf("%u%s", map_ids[i],
 210                               i == info.nr_map_ids - 1 ? "" : ",");
 211        }
 212}
 213
 214static void print_prog_json(struct bpf_prog_info *info, int fd)
 215{
 216        char *memlock;
 217
 218        jsonw_start_object(json_wtr);
 219        jsonw_uint_field(json_wtr, "id", info->id);
 220        if (info->type < ARRAY_SIZE(prog_type_name))
 221                jsonw_string_field(json_wtr, "type",
 222                                   prog_type_name[info->type]);
 223        else
 224                jsonw_uint_field(json_wtr, "type", info->type);
 225
 226        if (*info->name)
 227                jsonw_string_field(json_wtr, "name", info->name);
 228
 229        jsonw_name(json_wtr, "tag");
 230        jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"",
 231                     info->tag[0], info->tag[1], info->tag[2], info->tag[3],
 232                     info->tag[4], info->tag[5], info->tag[6], info->tag[7]);
 233
 234        print_dev_json(info->ifindex, info->netns_dev, info->netns_ino);
 235
 236        if (info->load_time) {
 237                char buf[32];
 238
 239                print_boot_time(info->load_time, buf, sizeof(buf));
 240
 241                /* Piggy back on load_time, since 0 uid is a valid one */
 242                jsonw_string_field(json_wtr, "loaded_at", buf);
 243                jsonw_uint_field(json_wtr, "uid", info->created_by_uid);
 244        }
 245
 246        jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len);
 247
 248        if (info->jited_prog_len) {
 249                jsonw_bool_field(json_wtr, "jited", true);
 250                jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len);
 251        } else {
 252                jsonw_bool_field(json_wtr, "jited", false);
 253        }
 254
 255        memlock = get_fdinfo(fd, "memlock");
 256        if (memlock)
 257                jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock));
 258        free(memlock);
 259
 260        if (info->nr_map_ids)
 261                show_prog_maps(fd, info->nr_map_ids);
 262
 263        if (!hash_empty(prog_table.table)) {
 264                struct pinned_obj *obj;
 265
 266                jsonw_name(json_wtr, "pinned");
 267                jsonw_start_array(json_wtr);
 268                hash_for_each_possible(prog_table.table, obj, hash, info->id) {
 269                        if (obj->id == info->id)
 270                                jsonw_string(json_wtr, obj->path);
 271                }
 272                jsonw_end_array(json_wtr);
 273        }
 274
 275        jsonw_end_object(json_wtr);
 276}
 277
 278static void print_prog_plain(struct bpf_prog_info *info, int fd)
 279{
 280        char *memlock;
 281
 282        printf("%u: ", info->id);
 283        if (info->type < ARRAY_SIZE(prog_type_name))
 284                printf("%s  ", prog_type_name[info->type]);
 285        else
 286                printf("type %u  ", info->type);
 287
 288        if (*info->name)
 289                printf("name %s  ", info->name);
 290
 291        printf("tag ");
 292        fprint_hex(stdout, info->tag, BPF_TAG_SIZE, "");
 293        print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino);
 294        printf("\n");
 295
 296        if (info->load_time) {
 297                char buf[32];
 298
 299                print_boot_time(info->load_time, buf, sizeof(buf));
 300
 301                /* Piggy back on load_time, since 0 uid is a valid one */
 302                printf("\tloaded_at %s  uid %u\n", buf, info->created_by_uid);
 303        }
 304
 305        printf("\txlated %uB", info->xlated_prog_len);
 306
 307        if (info->jited_prog_len)
 308                printf("  jited %uB", info->jited_prog_len);
 309        else
 310                printf("  not jited");
 311
 312        memlock = get_fdinfo(fd, "memlock");
 313        if (memlock)
 314                printf("  memlock %sB", memlock);
 315        free(memlock);
 316
 317        if (info->nr_map_ids)
 318                show_prog_maps(fd, info->nr_map_ids);
 319
 320        if (!hash_empty(prog_table.table)) {
 321                struct pinned_obj *obj;
 322
 323                printf("\n");
 324                hash_for_each_possible(prog_table.table, obj, hash, info->id) {
 325                        if (obj->id == info->id)
 326                                printf("\tpinned %s\n", obj->path);
 327                }
 328        }
 329
 330        printf("\n");
 331}
 332
 333static int show_prog(int fd)
 334{
 335        struct bpf_prog_info info = {};
 336        __u32 len = sizeof(info);
 337        int err;
 338
 339        err = bpf_obj_get_info_by_fd(fd, &info, &len);
 340        if (err) {
 341                p_err("can't get prog info: %s", strerror(errno));
 342                return -1;
 343        }
 344
 345        if (json_output)
 346                print_prog_json(&info, fd);
 347        else
 348                print_prog_plain(&info, fd);
 349
 350        return 0;
 351}
 352
 353static int do_show(int argc, char **argv)
 354{
 355        __u32 id = 0;
 356        int err;
 357        int fd;
 358
 359        if (show_pinned)
 360                build_pinned_obj_table(&prog_table, BPF_OBJ_PROG);
 361
 362        if (argc == 2) {
 363                fd = prog_parse_fd(&argc, &argv);
 364                if (fd < 0)
 365                        return -1;
 366
 367                return show_prog(fd);
 368        }
 369
 370        if (argc)
 371                return BAD_ARG();
 372
 373        if (json_output)
 374                jsonw_start_array(json_wtr);
 375        while (true) {
 376                err = bpf_prog_get_next_id(id, &id);
 377                if (err) {
 378                        if (errno == ENOENT) {
 379                                err = 0;
 380                                break;
 381                        }
 382                        p_err("can't get next program: %s%s", strerror(errno),
 383                              errno == EINVAL ? " -- kernel too old?" : "");
 384                        err = -1;
 385                        break;
 386                }
 387
 388                fd = bpf_prog_get_fd_by_id(id);
 389                if (fd < 0) {
 390                        if (errno == ENOENT)
 391                                continue;
 392                        p_err("can't get prog by id (%u): %s",
 393                              id, strerror(errno));
 394                        err = -1;
 395                        break;
 396                }
 397
 398                err = show_prog(fd);
 399                close(fd);
 400                if (err)
 401                        break;
 402        }
 403
 404        if (json_output)
 405                jsonw_end_array(json_wtr);
 406
 407        return err;
 408}
 409
 410#define SYM_MAX_NAME    256
 411
 412struct kernel_sym {
 413        unsigned long address;
 414        char name[SYM_MAX_NAME];
 415};
 416
 417struct dump_data {
 418        unsigned long address_call_base;
 419        struct kernel_sym *sym_mapping;
 420        __u32 sym_count;
 421        char scratch_buff[SYM_MAX_NAME];
 422};
 423
 424static int kernel_syms_cmp(const void *sym_a, const void *sym_b)
 425{
 426        return ((struct kernel_sym *)sym_a)->address -
 427               ((struct kernel_sym *)sym_b)->address;
 428}
 429
 430static void kernel_syms_load(struct dump_data *dd)
 431{
 432        struct kernel_sym *sym;
 433        char buff[256];
 434        void *tmp, *address;
 435        FILE *fp;
 436
 437        fp = fopen("/proc/kallsyms", "r");
 438        if (!fp)
 439                return;
 440
 441        while (!feof(fp)) {
 442                if (!fgets(buff, sizeof(buff), fp))
 443                        break;
 444                tmp = realloc(dd->sym_mapping,
 445                              (dd->sym_count + 1) *
 446                              sizeof(*dd->sym_mapping));
 447                if (!tmp) {
 448out:
 449                        free(dd->sym_mapping);
 450                        dd->sym_mapping = NULL;
 451                        fclose(fp);
 452                        return;
 453                }
 454                dd->sym_mapping = tmp;
 455                sym = &dd->sym_mapping[dd->sym_count];
 456                if (sscanf(buff, "%p %*c %s", &address, sym->name) != 2)
 457                        continue;
 458                sym->address = (unsigned long)address;
 459                if (!strcmp(sym->name, "__bpf_call_base")) {
 460                        dd->address_call_base = sym->address;
 461                        /* sysctl kernel.kptr_restrict was set */
 462                        if (!sym->address)
 463                                goto out;
 464                }
 465                if (sym->address)
 466                        dd->sym_count++;
 467        }
 468
 469        fclose(fp);
 470
 471        qsort(dd->sym_mapping, dd->sym_count,
 472              sizeof(*dd->sym_mapping), kernel_syms_cmp);
 473}
 474
 475static void kernel_syms_destroy(struct dump_data *dd)
 476{
 477        free(dd->sym_mapping);
 478}
 479
 480static struct kernel_sym *kernel_syms_search(struct dump_data *dd,
 481                                             unsigned long key)
 482{
 483        struct kernel_sym sym = {
 484                .address = key,
 485        };
 486
 487        return dd->sym_mapping ?
 488               bsearch(&sym, dd->sym_mapping, dd->sym_count,
 489                       sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL;
 490}
 491
 492static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...)
 493{
 494        va_list args;
 495
 496        va_start(args, fmt);
 497        vprintf(fmt, args);
 498        va_end(args);
 499}
 500
 501static const char *print_call_pcrel(struct dump_data *dd,
 502                                    struct kernel_sym *sym,
 503                                    unsigned long address,
 504                                    const struct bpf_insn *insn)
 505{
 506        if (sym)
 507                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 508                         "%+d#%s", insn->off, sym->name);
 509        else
 510                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 511                         "%+d#0x%lx", insn->off, address);
 512        return dd->scratch_buff;
 513}
 514
 515static const char *print_call_helper(struct dump_data *dd,
 516                                     struct kernel_sym *sym,
 517                                     unsigned long address)
 518{
 519        if (sym)
 520                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 521                         "%s", sym->name);
 522        else
 523                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 524                         "0x%lx", address);
 525        return dd->scratch_buff;
 526}
 527
 528static const char *print_call(void *private_data,
 529                              const struct bpf_insn *insn)
 530{
 531        struct dump_data *dd = private_data;
 532        unsigned long address = dd->address_call_base + insn->imm;
 533        struct kernel_sym *sym;
 534
 535        sym = kernel_syms_search(dd, address);
 536        if (insn->src_reg == BPF_PSEUDO_CALL)
 537                return print_call_pcrel(dd, sym, address, insn);
 538        else
 539                return print_call_helper(dd, sym, address);
 540}
 541
 542static const char *print_imm(void *private_data,
 543                             const struct bpf_insn *insn,
 544                             __u64 full_imm)
 545{
 546        struct dump_data *dd = private_data;
 547
 548        if (insn->src_reg == BPF_PSEUDO_MAP_FD)
 549                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 550                         "map[id:%u]", insn->imm);
 551        else
 552                snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
 553                         "0x%llx", (unsigned long long)full_imm);
 554        return dd->scratch_buff;
 555}
 556
 557static void dump_xlated_plain(struct dump_data *dd, void *buf,
 558                              unsigned int len, bool opcodes)
 559{
 560        const struct bpf_insn_cbs cbs = {
 561                .cb_print       = print_insn,
 562                .cb_call        = print_call,
 563                .cb_imm         = print_imm,
 564                .private_data   = dd,
 565        };
 566        struct bpf_insn *insn = buf;
 567        bool double_insn = false;
 568        unsigned int i;
 569
 570        for (i = 0; i < len / sizeof(*insn); i++) {
 571                if (double_insn) {
 572                        double_insn = false;
 573                        continue;
 574                }
 575
 576                double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
 577
 578                printf("% 4d: ", i);
 579                print_bpf_insn(&cbs, NULL, insn + i, true);
 580
 581                if (opcodes) {
 582                        printf("       ");
 583                        fprint_hex(stdout, insn + i, 8, " ");
 584                        if (double_insn && i < len - 1) {
 585                                printf(" ");
 586                                fprint_hex(stdout, insn + i + 1, 8, " ");
 587                        }
 588                        printf("\n");
 589                }
 590        }
 591}
 592
 593static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...)
 594{
 595        unsigned int l = strlen(fmt);
 596        char chomped_fmt[l];
 597        va_list args;
 598
 599        va_start(args, fmt);
 600        if (l > 0) {
 601                strncpy(chomped_fmt, fmt, l - 1);
 602                chomped_fmt[l - 1] = '\0';
 603        }
 604        jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
 605        va_end(args);
 606}
 607
 608static void dump_xlated_json(struct dump_data *dd, void *buf,
 609                             unsigned int len, bool opcodes)
 610{
 611        const struct bpf_insn_cbs cbs = {
 612                .cb_print       = print_insn_json,
 613                .cb_call        = print_call,
 614                .cb_imm         = print_imm,
 615                .private_data   = dd,
 616        };
 617        struct bpf_insn *insn = buf;
 618        bool double_insn = false;
 619        unsigned int i;
 620
 621        jsonw_start_array(json_wtr);
 622        for (i = 0; i < len / sizeof(*insn); i++) {
 623                if (double_insn) {
 624                        double_insn = false;
 625                        continue;
 626                }
 627                double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
 628
 629                jsonw_start_object(json_wtr);
 630                jsonw_name(json_wtr, "disasm");
 631                print_bpf_insn(&cbs, NULL, insn + i, true);
 632
 633                if (opcodes) {
 634                        jsonw_name(json_wtr, "opcodes");
 635                        jsonw_start_object(json_wtr);
 636
 637                        jsonw_name(json_wtr, "code");
 638                        jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
 639
 640                        jsonw_name(json_wtr, "src_reg");
 641                        jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
 642
 643                        jsonw_name(json_wtr, "dst_reg");
 644                        jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
 645
 646                        jsonw_name(json_wtr, "off");
 647                        print_hex_data_json((uint8_t *)(&insn[i].off), 2);
 648
 649                        jsonw_name(json_wtr, "imm");
 650                        if (double_insn && i < len - 1)
 651                                print_hex_data_json((uint8_t *)(&insn[i].imm),
 652                                                    12);
 653                        else
 654                                print_hex_data_json((uint8_t *)(&insn[i].imm),
 655                                                    4);
 656                        jsonw_end_object(json_wtr);
 657                }
 658                jsonw_end_object(json_wtr);
 659        }
 660        jsonw_end_array(json_wtr);
 661}
 662
 663static int do_dump(int argc, char **argv)
 664{
 665        struct bpf_prog_info info = {};
 666        struct dump_data dd = {};
 667        __u32 len = sizeof(info);
 668        unsigned int buf_size;
 669        char *filepath = NULL;
 670        bool opcodes = false;
 671        unsigned char *buf;
 672        __u32 *member_len;
 673        __u64 *member_ptr;
 674        ssize_t n;
 675        int err;
 676        int fd;
 677
 678        if (is_prefix(*argv, "jited")) {
 679                member_len = &info.jited_prog_len;
 680                member_ptr = &info.jited_prog_insns;
 681        } else if (is_prefix(*argv, "xlated")) {
 682                member_len = &info.xlated_prog_len;
 683                member_ptr = &info.xlated_prog_insns;
 684        } else {
 685                p_err("expected 'xlated' or 'jited', got: %s", *argv);
 686                return -1;
 687        }
 688        NEXT_ARG();
 689
 690        if (argc < 2)
 691                usage();
 692
 693        fd = prog_parse_fd(&argc, &argv);
 694        if (fd < 0)
 695                return -1;
 696
 697        if (is_prefix(*argv, "file")) {
 698                NEXT_ARG();
 699                if (!argc) {
 700                        p_err("expected file path");
 701                        return -1;
 702                }
 703
 704                filepath = *argv;
 705                NEXT_ARG();
 706        } else if (is_prefix(*argv, "opcodes")) {
 707                opcodes = true;
 708                NEXT_ARG();
 709        }
 710
 711        if (argc) {
 712                usage();
 713                return -1;
 714        }
 715
 716        err = bpf_obj_get_info_by_fd(fd, &info, &len);
 717        if (err) {
 718                p_err("can't get prog info: %s", strerror(errno));
 719                return -1;
 720        }
 721
 722        if (!*member_len) {
 723                p_info("no instructions returned");
 724                close(fd);
 725                return 0;
 726        }
 727
 728        buf_size = *member_len;
 729
 730        buf = malloc(buf_size);
 731        if (!buf) {
 732                p_err("mem alloc failed");
 733                close(fd);
 734                return -1;
 735        }
 736
 737        memset(&info, 0, sizeof(info));
 738
 739        *member_ptr = ptr_to_u64(buf);
 740        *member_len = buf_size;
 741
 742        err = bpf_obj_get_info_by_fd(fd, &info, &len);
 743        close(fd);
 744        if (err) {
 745                p_err("can't get prog info: %s", strerror(errno));
 746                goto err_free;
 747        }
 748
 749        if (*member_len > buf_size) {
 750                p_err("too many instructions returned");
 751                goto err_free;
 752        }
 753
 754        if ((member_len == &info.jited_prog_len &&
 755             info.jited_prog_insns == 0) ||
 756            (member_len == &info.xlated_prog_len &&
 757             info.xlated_prog_insns == 0)) {
 758                p_err("error retrieving insn dump: kernel.kptr_restrict set?");
 759                goto err_free;
 760        }
 761
 762        if (filepath) {
 763                fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
 764                if (fd < 0) {
 765                        p_err("can't open file %s: %s", filepath,
 766                              strerror(errno));
 767                        goto err_free;
 768                }
 769
 770                n = write(fd, buf, *member_len);
 771                close(fd);
 772                if (n != *member_len) {
 773                        p_err("error writing output file: %s",
 774                              n < 0 ? strerror(errno) : "short write");
 775                        goto err_free;
 776                }
 777
 778                if (json_output)
 779                        jsonw_null(json_wtr);
 780        } else {
 781                if (member_len == &info.jited_prog_len) {
 782                        const char *name = NULL;
 783
 784                        if (info.ifindex) {
 785                                name = ifindex_to_bfd_name_ns(info.ifindex,
 786                                                              info.netns_dev,
 787                                                              info.netns_ino);
 788                                if (!name)
 789                                        goto err_free;
 790                        }
 791
 792                        disasm_print_insn(buf, *member_len, opcodes, name);
 793                } else {
 794                        kernel_syms_load(&dd);
 795                        if (json_output)
 796                                dump_xlated_json(&dd, buf, *member_len, opcodes);
 797                        else
 798                                dump_xlated_plain(&dd, buf, *member_len, opcodes);
 799                        kernel_syms_destroy(&dd);
 800                }
 801        }
 802
 803        free(buf);
 804        return 0;
 805
 806err_free:
 807        free(buf);
 808        return -1;
 809}
 810
 811static int do_pin(int argc, char **argv)
 812{
 813        int err;
 814
 815        err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id);
 816        if (!err && json_output)
 817                jsonw_null(json_wtr);
 818        return err;
 819}
 820
 821static int do_load(int argc, char **argv)
 822{
 823        struct bpf_object *obj;
 824        int prog_fd;
 825
 826        if (argc != 2)
 827                usage();
 828
 829        if (bpf_prog_load(argv[0], BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) {
 830                p_err("failed to load program");
 831                return -1;
 832        }
 833
 834        if (do_pin_fd(prog_fd, argv[1])) {
 835                p_err("failed to pin program");
 836                return -1;
 837        }
 838
 839        if (json_output)
 840                jsonw_null(json_wtr);
 841
 842        return 0;
 843}
 844
 845static int do_help(int argc, char **argv)
 846{
 847        if (json_output) {
 848                jsonw_null(json_wtr);
 849                return 0;
 850        }
 851
 852        fprintf(stderr,
 853                "Usage: %s %s { show | list } [PROG]\n"
 854                "       %s %s dump xlated PROG [{ file FILE | opcodes }]\n"
 855                "       %s %s dump jited  PROG [{ file FILE | opcodes }]\n"
 856                "       %s %s pin   PROG FILE\n"
 857                "       %s %s load  OBJ  FILE\n"
 858                "       %s %s help\n"
 859                "\n"
 860                "       " HELP_SPEC_PROGRAM "\n"
 861                "       " HELP_SPEC_OPTIONS "\n"
 862                "",
 863                bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
 864                bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);
 865
 866        return 0;
 867}
 868
 869static const struct cmd cmds[] = {
 870        { "show",       do_show },
 871        { "list",       do_show },
 872        { "help",       do_help },
 873        { "dump",       do_dump },
 874        { "pin",        do_pin },
 875        { "load",       do_load },
 876        { 0 }
 877};
 878
 879int do_prog(int argc, char **argv)
 880{
 881        return cmd_select(cmds, argc, argv, do_help);
 882}
 883