Merge tag 'for-5.5/dm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device...
[platform/kernel/linux-rpi.git] / tools / bpf / bpftool / btf.c
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (C) 2019 Facebook */
3
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <linux/err.h>
7 #include <stdbool.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <bpf.h>
12 #include <libbpf.h>
13 #include <linux/btf.h>
14 #include <linux/hashtable.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18
19 #include "btf.h"
20 #include "json_writer.h"
21 #include "main.h"
22
23 static const char * const btf_kind_str[NR_BTF_KINDS] = {
24         [BTF_KIND_UNKN]         = "UNKNOWN",
25         [BTF_KIND_INT]          = "INT",
26         [BTF_KIND_PTR]          = "PTR",
27         [BTF_KIND_ARRAY]        = "ARRAY",
28         [BTF_KIND_STRUCT]       = "STRUCT",
29         [BTF_KIND_UNION]        = "UNION",
30         [BTF_KIND_ENUM]         = "ENUM",
31         [BTF_KIND_FWD]          = "FWD",
32         [BTF_KIND_TYPEDEF]      = "TYPEDEF",
33         [BTF_KIND_VOLATILE]     = "VOLATILE",
34         [BTF_KIND_CONST]        = "CONST",
35         [BTF_KIND_RESTRICT]     = "RESTRICT",
36         [BTF_KIND_FUNC]         = "FUNC",
37         [BTF_KIND_FUNC_PROTO]   = "FUNC_PROTO",
38         [BTF_KIND_VAR]          = "VAR",
39         [BTF_KIND_DATASEC]      = "DATASEC",
40 };
41
42 struct btf_attach_table {
43         DECLARE_HASHTABLE(table, 16);
44 };
45
46 struct btf_attach_point {
47         __u32 obj_id;
48         __u32 btf_id;
49         struct hlist_node hash;
50 };
51
52 static const char *btf_int_enc_str(__u8 encoding)
53 {
54         switch (encoding) {
55         case 0:
56                 return "(none)";
57         case BTF_INT_SIGNED:
58                 return "SIGNED";
59         case BTF_INT_CHAR:
60                 return "CHAR";
61         case BTF_INT_BOOL:
62                 return "BOOL";
63         default:
64                 return "UNKN";
65         }
66 }
67
68 static const char *btf_var_linkage_str(__u32 linkage)
69 {
70         switch (linkage) {
71         case BTF_VAR_STATIC:
72                 return "static";
73         case BTF_VAR_GLOBAL_ALLOCATED:
74                 return "global-alloc";
75         default:
76                 return "(unknown)";
77         }
78 }
79
80 static const char *btf_str(const struct btf *btf, __u32 off)
81 {
82         if (!off)
83                 return "(anon)";
84         return btf__name_by_offset(btf, off) ? : "(invalid)";
85 }
86
87 static int dump_btf_type(const struct btf *btf, __u32 id,
88                          const struct btf_type *t)
89 {
90         json_writer_t *w = json_wtr;
91         int kind, safe_kind;
92
93         kind = BTF_INFO_KIND(t->info);
94         safe_kind = kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN;
95
96         if (json_output) {
97                 jsonw_start_object(w);
98                 jsonw_uint_field(w, "id", id);
99                 jsonw_string_field(w, "kind", btf_kind_str[safe_kind]);
100                 jsonw_string_field(w, "name", btf_str(btf, t->name_off));
101         } else {
102                 printf("[%u] %s '%s'", id, btf_kind_str[safe_kind],
103                        btf_str(btf, t->name_off));
104         }
105
106         switch (BTF_INFO_KIND(t->info)) {
107         case BTF_KIND_INT: {
108                 __u32 v = *(__u32 *)(t + 1);
109                 const char *enc;
110
111                 enc = btf_int_enc_str(BTF_INT_ENCODING(v));
112
113                 if (json_output) {
114                         jsonw_uint_field(w, "size", t->size);
115                         jsonw_uint_field(w, "bits_offset", BTF_INT_OFFSET(v));
116                         jsonw_uint_field(w, "nr_bits", BTF_INT_BITS(v));
117                         jsonw_string_field(w, "encoding", enc);
118                 } else {
119                         printf(" size=%u bits_offset=%u nr_bits=%u encoding=%s",
120                                t->size, BTF_INT_OFFSET(v), BTF_INT_BITS(v),
121                                enc);
122                 }
123                 break;
124         }
125         case BTF_KIND_PTR:
126         case BTF_KIND_CONST:
127         case BTF_KIND_VOLATILE:
128         case BTF_KIND_RESTRICT:
129         case BTF_KIND_TYPEDEF:
130                 if (json_output)
131                         jsonw_uint_field(w, "type_id", t->type);
132                 else
133                         printf(" type_id=%u", t->type);
134                 break;
135         case BTF_KIND_ARRAY: {
136                 const struct btf_array *arr = (const void *)(t + 1);
137
138                 if (json_output) {
139                         jsonw_uint_field(w, "type_id", arr->type);
140                         jsonw_uint_field(w, "index_type_id", arr->index_type);
141                         jsonw_uint_field(w, "nr_elems", arr->nelems);
142                 } else {
143                         printf(" type_id=%u index_type_id=%u nr_elems=%u",
144                                arr->type, arr->index_type, arr->nelems);
145                 }
146                 break;
147         }
148         case BTF_KIND_STRUCT:
149         case BTF_KIND_UNION: {
150                 const struct btf_member *m = (const void *)(t + 1);
151                 __u16 vlen = BTF_INFO_VLEN(t->info);
152                 int i;
153
154                 if (json_output) {
155                         jsonw_uint_field(w, "size", t->size);
156                         jsonw_uint_field(w, "vlen", vlen);
157                         jsonw_name(w, "members");
158                         jsonw_start_array(w);
159                 } else {
160                         printf(" size=%u vlen=%u", t->size, vlen);
161                 }
162                 for (i = 0; i < vlen; i++, m++) {
163                         const char *name = btf_str(btf, m->name_off);
164                         __u32 bit_off, bit_sz;
165
166                         if (BTF_INFO_KFLAG(t->info)) {
167                                 bit_off = BTF_MEMBER_BIT_OFFSET(m->offset);
168                                 bit_sz = BTF_MEMBER_BITFIELD_SIZE(m->offset);
169                         } else {
170                                 bit_off = m->offset;
171                                 bit_sz = 0;
172                         }
173
174                         if (json_output) {
175                                 jsonw_start_object(w);
176                                 jsonw_string_field(w, "name", name);
177                                 jsonw_uint_field(w, "type_id", m->type);
178                                 jsonw_uint_field(w, "bits_offset", bit_off);
179                                 if (bit_sz) {
180                                         jsonw_uint_field(w, "bitfield_size",
181                                                          bit_sz);
182                                 }
183                                 jsonw_end_object(w);
184                         } else {
185                                 printf("\n\t'%s' type_id=%u bits_offset=%u",
186                                        name, m->type, bit_off);
187                                 if (bit_sz)
188                                         printf(" bitfield_size=%u", bit_sz);
189                         }
190                 }
191                 if (json_output)
192                         jsonw_end_array(w);
193                 break;
194         }
195         case BTF_KIND_ENUM: {
196                 const struct btf_enum *v = (const void *)(t + 1);
197                 __u16 vlen = BTF_INFO_VLEN(t->info);
198                 int i;
199
200                 if (json_output) {
201                         jsonw_uint_field(w, "size", t->size);
202                         jsonw_uint_field(w, "vlen", vlen);
203                         jsonw_name(w, "values");
204                         jsonw_start_array(w);
205                 } else {
206                         printf(" size=%u vlen=%u", t->size, vlen);
207                 }
208                 for (i = 0; i < vlen; i++, v++) {
209                         const char *name = btf_str(btf, v->name_off);
210
211                         if (json_output) {
212                                 jsonw_start_object(w);
213                                 jsonw_string_field(w, "name", name);
214                                 jsonw_uint_field(w, "val", v->val);
215                                 jsonw_end_object(w);
216                         } else {
217                                 printf("\n\t'%s' val=%u", name, v->val);
218                         }
219                 }
220                 if (json_output)
221                         jsonw_end_array(w);
222                 break;
223         }
224         case BTF_KIND_FWD: {
225                 const char *fwd_kind = BTF_INFO_KFLAG(t->info) ? "union"
226                                                                : "struct";
227
228                 if (json_output)
229                         jsonw_string_field(w, "fwd_kind", fwd_kind);
230                 else
231                         printf(" fwd_kind=%s", fwd_kind);
232                 break;
233         }
234         case BTF_KIND_FUNC:
235                 if (json_output)
236                         jsonw_uint_field(w, "type_id", t->type);
237                 else
238                         printf(" type_id=%u", t->type);
239                 break;
240         case BTF_KIND_FUNC_PROTO: {
241                 const struct btf_param *p = (const void *)(t + 1);
242                 __u16 vlen = BTF_INFO_VLEN(t->info);
243                 int i;
244
245                 if (json_output) {
246                         jsonw_uint_field(w, "ret_type_id", t->type);
247                         jsonw_uint_field(w, "vlen", vlen);
248                         jsonw_name(w, "params");
249                         jsonw_start_array(w);
250                 } else {
251                         printf(" ret_type_id=%u vlen=%u", t->type, vlen);
252                 }
253                 for (i = 0; i < vlen; i++, p++) {
254                         const char *name = btf_str(btf, p->name_off);
255
256                         if (json_output) {
257                                 jsonw_start_object(w);
258                                 jsonw_string_field(w, "name", name);
259                                 jsonw_uint_field(w, "type_id", p->type);
260                                 jsonw_end_object(w);
261                         } else {
262                                 printf("\n\t'%s' type_id=%u", name, p->type);
263                         }
264                 }
265                 if (json_output)
266                         jsonw_end_array(w);
267                 break;
268         }
269         case BTF_KIND_VAR: {
270                 const struct btf_var *v = (const void *)(t + 1);
271                 const char *linkage;
272
273                 linkage = btf_var_linkage_str(v->linkage);
274
275                 if (json_output) {
276                         jsonw_uint_field(w, "type_id", t->type);
277                         jsonw_string_field(w, "linkage", linkage);
278                 } else {
279                         printf(" type_id=%u, linkage=%s", t->type, linkage);
280                 }
281                 break;
282         }
283         case BTF_KIND_DATASEC: {
284                 const struct btf_var_secinfo *v = (const void *)(t+1);
285                 __u16 vlen = BTF_INFO_VLEN(t->info);
286                 int i;
287
288                 if (json_output) {
289                         jsonw_uint_field(w, "size", t->size);
290                         jsonw_uint_field(w, "vlen", vlen);
291                         jsonw_name(w, "vars");
292                         jsonw_start_array(w);
293                 } else {
294                         printf(" size=%u vlen=%u", t->size, vlen);
295                 }
296                 for (i = 0; i < vlen; i++, v++) {
297                         if (json_output) {
298                                 jsonw_start_object(w);
299                                 jsonw_uint_field(w, "type_id", v->type);
300                                 jsonw_uint_field(w, "offset", v->offset);
301                                 jsonw_uint_field(w, "size", v->size);
302                                 jsonw_end_object(w);
303                         } else {
304                                 printf("\n\ttype_id=%u offset=%u size=%u",
305                                        v->type, v->offset, v->size);
306                         }
307                 }
308                 if (json_output)
309                         jsonw_end_array(w);
310                 break;
311         }
312         default:
313                 break;
314         }
315
316         if (json_output)
317                 jsonw_end_object(json_wtr);
318         else
319                 printf("\n");
320
321         return 0;
322 }
323
324 static int dump_btf_raw(const struct btf *btf,
325                         __u32 *root_type_ids, int root_type_cnt)
326 {
327         const struct btf_type *t;
328         int i;
329
330         if (json_output) {
331                 jsonw_start_object(json_wtr);
332                 jsonw_name(json_wtr, "types");
333                 jsonw_start_array(json_wtr);
334         }
335
336         if (root_type_cnt) {
337                 for (i = 0; i < root_type_cnt; i++) {
338                         t = btf__type_by_id(btf, root_type_ids[i]);
339                         dump_btf_type(btf, root_type_ids[i], t);
340                 }
341         } else {
342                 int cnt = btf__get_nr_types(btf);
343
344                 for (i = 1; i <= cnt; i++) {
345                         t = btf__type_by_id(btf, i);
346                         dump_btf_type(btf, i, t);
347                 }
348         }
349
350         if (json_output) {
351                 jsonw_end_array(json_wtr);
352                 jsonw_end_object(json_wtr);
353         }
354         return 0;
355 }
356
357 static void __printf(2, 0) btf_dump_printf(void *ctx,
358                                            const char *fmt, va_list args)
359 {
360         vfprintf(stdout, fmt, args);
361 }
362
363 static int dump_btf_c(const struct btf *btf,
364                       __u32 *root_type_ids, int root_type_cnt)
365 {
366         struct btf_dump *d;
367         int err = 0, i;
368
369         d = btf_dump__new(btf, NULL, NULL, btf_dump_printf);
370         if (IS_ERR(d))
371                 return PTR_ERR(d);
372
373         if (root_type_cnt) {
374                 for (i = 0; i < root_type_cnt; i++) {
375                         err = btf_dump__dump_type(d, root_type_ids[i]);
376                         if (err)
377                                 goto done;
378                 }
379         } else {
380                 int cnt = btf__get_nr_types(btf);
381
382                 for (i = 1; i <= cnt; i++) {
383                         err = btf_dump__dump_type(d, i);
384                         if (err)
385                                 goto done;
386                 }
387         }
388
389 done:
390         btf_dump__free(d);
391         return err;
392 }
393
394 static struct btf *btf__parse_raw(const char *file)
395 {
396         struct btf *btf;
397         struct stat st;
398         __u8 *buf;
399         FILE *f;
400
401         if (stat(file, &st))
402                 return NULL;
403
404         f = fopen(file, "rb");
405         if (!f)
406                 return NULL;
407
408         buf = malloc(st.st_size);
409         if (!buf) {
410                 btf = ERR_PTR(-ENOMEM);
411                 goto exit_close;
412         }
413
414         if ((size_t) st.st_size != fread(buf, 1, st.st_size, f)) {
415                 btf = ERR_PTR(-EINVAL);
416                 goto exit_free;
417         }
418
419         btf = btf__new(buf, st.st_size);
420
421 exit_free:
422         free(buf);
423 exit_close:
424         fclose(f);
425         return btf;
426 }
427
428 static bool is_btf_raw(const char *file)
429 {
430         __u16 magic = 0;
431         int fd, nb_read;
432
433         fd = open(file, O_RDONLY);
434         if (fd < 0)
435                 return false;
436
437         nb_read = read(fd, &magic, sizeof(magic));
438         close(fd);
439         return nb_read == sizeof(magic) && magic == BTF_MAGIC;
440 }
441
442 static int do_dump(int argc, char **argv)
443 {
444         struct btf *btf = NULL;
445         __u32 root_type_ids[2];
446         int root_type_cnt = 0;
447         bool dump_c = false;
448         __u32 btf_id = -1;
449         const char *src;
450         int fd = -1;
451         int err;
452
453         if (!REQ_ARGS(2)) {
454                 usage();
455                 return -1;
456         }
457         src = GET_ARG();
458
459         if (is_prefix(src, "map")) {
460                 struct bpf_map_info info = {};
461                 __u32 len = sizeof(info);
462
463                 if (!REQ_ARGS(2)) {
464                         usage();
465                         return -1;
466                 }
467
468                 fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
469                 if (fd < 0)
470                         return -1;
471
472                 btf_id = info.btf_id;
473                 if (argc && is_prefix(*argv, "key")) {
474                         root_type_ids[root_type_cnt++] = info.btf_key_type_id;
475                         NEXT_ARG();
476                 } else if (argc && is_prefix(*argv, "value")) {
477                         root_type_ids[root_type_cnt++] = info.btf_value_type_id;
478                         NEXT_ARG();
479                 } else if (argc && is_prefix(*argv, "all")) {
480                         NEXT_ARG();
481                 } else if (argc && is_prefix(*argv, "kv")) {
482                         root_type_ids[root_type_cnt++] = info.btf_key_type_id;
483                         root_type_ids[root_type_cnt++] = info.btf_value_type_id;
484                         NEXT_ARG();
485                 } else {
486                         root_type_ids[root_type_cnt++] = info.btf_key_type_id;
487                         root_type_ids[root_type_cnt++] = info.btf_value_type_id;
488                 }
489         } else if (is_prefix(src, "prog")) {
490                 struct bpf_prog_info info = {};
491                 __u32 len = sizeof(info);
492
493                 if (!REQ_ARGS(2)) {
494                         usage();
495                         return -1;
496                 }
497
498                 fd = prog_parse_fd(&argc, &argv);
499                 if (fd < 0)
500                         return -1;
501
502                 err = bpf_obj_get_info_by_fd(fd, &info, &len);
503                 if (err) {
504                         p_err("can't get prog info: %s", strerror(errno));
505                         goto done;
506                 }
507
508                 btf_id = info.btf_id;
509         } else if (is_prefix(src, "id")) {
510                 char *endptr;
511
512                 btf_id = strtoul(*argv, &endptr, 0);
513                 if (*endptr) {
514                         p_err("can't parse %s as ID", *argv);
515                         return -1;
516                 }
517                 NEXT_ARG();
518         } else if (is_prefix(src, "file")) {
519                 if (is_btf_raw(*argv))
520                         btf = btf__parse_raw(*argv);
521                 else
522                         btf = btf__parse_elf(*argv, NULL);
523
524                 if (IS_ERR(btf)) {
525                         err = PTR_ERR(btf);
526                         btf = NULL;
527                         p_err("failed to load BTF from %s: %s", 
528                               *argv, strerror(err));
529                         goto done;
530                 }
531                 NEXT_ARG();
532         } else {
533                 err = -1;
534                 p_err("unrecognized BTF source specifier: '%s'", src);
535                 goto done;
536         }
537
538         while (argc) {
539                 if (is_prefix(*argv, "format")) {
540                         NEXT_ARG();
541                         if (argc < 1) {
542                                 p_err("expecting value for 'format' option\n");
543                                 goto done;
544                         }
545                         if (strcmp(*argv, "c") == 0) {
546                                 dump_c = true;
547                         } else if (strcmp(*argv, "raw") == 0) {
548                                 dump_c = false;
549                         } else {
550                                 p_err("unrecognized format specifier: '%s', possible values: raw, c",
551                                       *argv);
552                                 goto done;
553                         }
554                         NEXT_ARG();
555                 } else {
556                         p_err("unrecognized option: '%s'", *argv);
557                         goto done;
558                 }
559         }
560
561         if (!btf) {
562                 err = btf__get_from_id(btf_id, &btf);
563                 if (err) {
564                         p_err("get btf by id (%u): %s", btf_id, strerror(err));
565                         goto done;
566                 }
567                 if (!btf) {
568                         err = ENOENT;
569                         p_err("can't find btf with ID (%u)", btf_id);
570                         goto done;
571                 }
572         }
573
574         if (dump_c) {
575                 if (json_output) {
576                         p_err("JSON output for C-syntax dump is not supported");
577                         err = -ENOTSUP;
578                         goto done;
579                 }
580                 err = dump_btf_c(btf, root_type_ids, root_type_cnt);
581         } else {
582                 err = dump_btf_raw(btf, root_type_ids, root_type_cnt);
583         }
584
585 done:
586         close(fd);
587         btf__free(btf);
588         return err;
589 }
590
591 static int btf_parse_fd(int *argc, char ***argv)
592 {
593         unsigned int id;
594         char *endptr;
595         int fd;
596
597         if (!is_prefix(*argv[0], "id")) {
598                 p_err("expected 'id', got: '%s'?", **argv);
599                 return -1;
600         }
601         NEXT_ARGP();
602
603         id = strtoul(**argv, &endptr, 0);
604         if (*endptr) {
605                 p_err("can't parse %s as ID", **argv);
606                 return -1;
607         }
608         NEXT_ARGP();
609
610         fd = bpf_btf_get_fd_by_id(id);
611         if (fd < 0)
612                 p_err("can't get BTF object by id (%u): %s",
613                       id, strerror(errno));
614
615         return fd;
616 }
617
618 static void delete_btf_table(struct btf_attach_table *tab)
619 {
620         struct btf_attach_point *obj;
621         struct hlist_node *tmp;
622
623         unsigned int bkt;
624
625         hash_for_each_safe(tab->table, bkt, tmp, obj, hash) {
626                 hash_del(&obj->hash);
627                 free(obj);
628         }
629 }
630
631 static int
632 build_btf_type_table(struct btf_attach_table *tab, enum bpf_obj_type type,
633                      void *info, __u32 *len)
634 {
635         static const char * const names[] = {
636                 [BPF_OBJ_UNKNOWN]       = "unknown",
637                 [BPF_OBJ_PROG]          = "prog",
638                 [BPF_OBJ_MAP]           = "map",
639         };
640         struct btf_attach_point *obj_node;
641         __u32 btf_id, id = 0;
642         int err;
643         int fd;
644
645         while (true) {
646                 switch (type) {
647                 case BPF_OBJ_PROG:
648                         err = bpf_prog_get_next_id(id, &id);
649                         break;
650                 case BPF_OBJ_MAP:
651                         err = bpf_map_get_next_id(id, &id);
652                         break;
653                 default:
654                         err = -1;
655                         p_err("unexpected object type: %d", type);
656                         goto err_free;
657                 }
658                 if (err) {
659                         if (errno == ENOENT) {
660                                 err = 0;
661                                 break;
662                         }
663                         p_err("can't get next %s: %s%s", names[type],
664                               strerror(errno),
665                               errno == EINVAL ? " -- kernel too old?" : "");
666                         goto err_free;
667                 }
668
669                 switch (type) {
670                 case BPF_OBJ_PROG:
671                         fd = bpf_prog_get_fd_by_id(id);
672                         break;
673                 case BPF_OBJ_MAP:
674                         fd = bpf_map_get_fd_by_id(id);
675                         break;
676                 default:
677                         err = -1;
678                         p_err("unexpected object type: %d", type);
679                         goto err_free;
680                 }
681                 if (fd < 0) {
682                         if (errno == ENOENT)
683                                 continue;
684                         p_err("can't get %s by id (%u): %s", names[type], id,
685                               strerror(errno));
686                         err = -1;
687                         goto err_free;
688                 }
689
690                 memset(info, 0, *len);
691                 err = bpf_obj_get_info_by_fd(fd, info, len);
692                 close(fd);
693                 if (err) {
694                         p_err("can't get %s info: %s", names[type],
695                               strerror(errno));
696                         goto err_free;
697                 }
698
699                 switch (type) {
700                 case BPF_OBJ_PROG:
701                         btf_id = ((struct bpf_prog_info *)info)->btf_id;
702                         break;
703                 case BPF_OBJ_MAP:
704                         btf_id = ((struct bpf_map_info *)info)->btf_id;
705                         break;
706                 default:
707                         err = -1;
708                         p_err("unexpected object type: %d", type);
709                         goto err_free;
710                 }
711                 if (!btf_id)
712                         continue;
713
714                 obj_node = calloc(1, sizeof(*obj_node));
715                 if (!obj_node) {
716                         p_err("failed to allocate memory: %s", strerror(errno));
717                         goto err_free;
718                 }
719
720                 obj_node->obj_id = id;
721                 obj_node->btf_id = btf_id;
722                 hash_add(tab->table, &obj_node->hash, obj_node->btf_id);
723         }
724
725         return 0;
726
727 err_free:
728         delete_btf_table(tab);
729         return err;
730 }
731
732 static int
733 build_btf_tables(struct btf_attach_table *btf_prog_table,
734                  struct btf_attach_table *btf_map_table)
735 {
736         struct bpf_prog_info prog_info;
737         __u32 prog_len = sizeof(prog_info);
738         struct bpf_map_info map_info;
739         __u32 map_len = sizeof(map_info);
740         int err = 0;
741
742         err = build_btf_type_table(btf_prog_table, BPF_OBJ_PROG, &prog_info,
743                                    &prog_len);
744         if (err)
745                 return err;
746
747         err = build_btf_type_table(btf_map_table, BPF_OBJ_MAP, &map_info,
748                                    &map_len);
749         if (err) {
750                 delete_btf_table(btf_prog_table);
751                 return err;
752         }
753
754         return 0;
755 }
756
757 static void
758 show_btf_plain(struct bpf_btf_info *info, int fd,
759                struct btf_attach_table *btf_prog_table,
760                struct btf_attach_table *btf_map_table)
761 {
762         struct btf_attach_point *obj;
763         int n;
764
765         printf("%u: ", info->id);
766         printf("size %uB", info->btf_size);
767
768         n = 0;
769         hash_for_each_possible(btf_prog_table->table, obj, hash, info->id) {
770                 if (obj->btf_id == info->id)
771                         printf("%s%u", n++ == 0 ? "  prog_ids " : ",",
772                                obj->obj_id);
773         }
774
775         n = 0;
776         hash_for_each_possible(btf_map_table->table, obj, hash, info->id) {
777                 if (obj->btf_id == info->id)
778                         printf("%s%u", n++ == 0 ? "  map_ids " : ",",
779                                obj->obj_id);
780         }
781
782         printf("\n");
783 }
784
785 static void
786 show_btf_json(struct bpf_btf_info *info, int fd,
787               struct btf_attach_table *btf_prog_table,
788               struct btf_attach_table *btf_map_table)
789 {
790         struct btf_attach_point *obj;
791
792         jsonw_start_object(json_wtr);   /* btf object */
793         jsonw_uint_field(json_wtr, "id", info->id);
794         jsonw_uint_field(json_wtr, "size", info->btf_size);
795
796         jsonw_name(json_wtr, "prog_ids");
797         jsonw_start_array(json_wtr);    /* prog_ids */
798         hash_for_each_possible(btf_prog_table->table, obj, hash,
799                                info->id) {
800                 if (obj->btf_id == info->id)
801                         jsonw_uint(json_wtr, obj->obj_id);
802         }
803         jsonw_end_array(json_wtr);      /* prog_ids */
804
805         jsonw_name(json_wtr, "map_ids");
806         jsonw_start_array(json_wtr);    /* map_ids */
807         hash_for_each_possible(btf_map_table->table, obj, hash,
808                                info->id) {
809                 if (obj->btf_id == info->id)
810                         jsonw_uint(json_wtr, obj->obj_id);
811         }
812         jsonw_end_array(json_wtr);      /* map_ids */
813         jsonw_end_object(json_wtr);     /* btf object */
814 }
815
816 static int
817 show_btf(int fd, struct btf_attach_table *btf_prog_table,
818          struct btf_attach_table *btf_map_table)
819 {
820         struct bpf_btf_info info = {};
821         __u32 len = sizeof(info);
822         int err;
823
824         err = bpf_obj_get_info_by_fd(fd, &info, &len);
825         if (err) {
826                 p_err("can't get BTF object info: %s", strerror(errno));
827                 return -1;
828         }
829
830         if (json_output)
831                 show_btf_json(&info, fd, btf_prog_table, btf_map_table);
832         else
833                 show_btf_plain(&info, fd, btf_prog_table, btf_map_table);
834
835         return 0;
836 }
837
838 static int do_show(int argc, char **argv)
839 {
840         struct btf_attach_table btf_prog_table;
841         struct btf_attach_table btf_map_table;
842         int err, fd = -1;
843         __u32 id = 0;
844
845         if (argc == 2) {
846                 fd = btf_parse_fd(&argc, &argv);
847                 if (fd < 0)
848                         return -1;
849         }
850
851         if (argc) {
852                 if (fd >= 0)
853                         close(fd);
854                 return BAD_ARG();
855         }
856
857         hash_init(btf_prog_table.table);
858         hash_init(btf_map_table.table);
859         err = build_btf_tables(&btf_prog_table, &btf_map_table);
860         if (err) {
861                 if (fd >= 0)
862                         close(fd);
863                 return err;
864         }
865
866         if (fd >= 0) {
867                 err = show_btf(fd, &btf_prog_table, &btf_map_table);
868                 close(fd);
869                 goto exit_free;
870         }
871
872         if (json_output)
873                 jsonw_start_array(json_wtr);    /* root array */
874
875         while (true) {
876                 err = bpf_btf_get_next_id(id, &id);
877                 if (err) {
878                         if (errno == ENOENT) {
879                                 err = 0;
880                                 break;
881                         }
882                         p_err("can't get next BTF object: %s%s",
883                               strerror(errno),
884                               errno == EINVAL ? " -- kernel too old?" : "");
885                         err = -1;
886                         break;
887                 }
888
889                 fd = bpf_btf_get_fd_by_id(id);
890                 if (fd < 0) {
891                         if (errno == ENOENT)
892                                 continue;
893                         p_err("can't get BTF object by id (%u): %s",
894                               id, strerror(errno));
895                         err = -1;
896                         break;
897                 }
898
899                 err = show_btf(fd, &btf_prog_table, &btf_map_table);
900                 close(fd);
901                 if (err)
902                         break;
903         }
904
905         if (json_output)
906                 jsonw_end_array(json_wtr);      /* root array */
907
908 exit_free:
909         delete_btf_table(&btf_prog_table);
910         delete_btf_table(&btf_map_table);
911
912         return err;
913 }
914
915 static int do_help(int argc, char **argv)
916 {
917         if (json_output) {
918                 jsonw_null(json_wtr);
919                 return 0;
920         }
921
922         fprintf(stderr,
923                 "Usage: %s btf { show | list } [id BTF_ID]\n"
924                 "       %s btf dump BTF_SRC [format FORMAT]\n"
925                 "       %s btf help\n"
926                 "\n"
927                 "       BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
928                 "       FORMAT  := { raw | c }\n"
929                 "       " HELP_SPEC_MAP "\n"
930                 "       " HELP_SPEC_PROGRAM "\n"
931                 "       " HELP_SPEC_OPTIONS "\n"
932                 "",
933                 bin_name, bin_name, bin_name);
934
935         return 0;
936 }
937
938 static const struct cmd cmds[] = {
939         { "show",       do_show },
940         { "list",       do_show },
941         { "help",       do_help },
942         { "dump",       do_dump },
943         { 0 }
944 };
945
946 int do_btf(int argc, char **argv)
947 {
948         return cmd_select(cmds, argc, argv, do_help);
949 }