1
2
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
15#include "btf.h"
16#include "json_writer.h"
17#include "main.h"
18
19static const char * const btf_kind_str[NR_BTF_KINDS] = {
20 [BTF_KIND_UNKN] = "UNKNOWN",
21 [BTF_KIND_INT] = "INT",
22 [BTF_KIND_PTR] = "PTR",
23 [BTF_KIND_ARRAY] = "ARRAY",
24 [BTF_KIND_STRUCT] = "STRUCT",
25 [BTF_KIND_UNION] = "UNION",
26 [BTF_KIND_ENUM] = "ENUM",
27 [BTF_KIND_FWD] = "FWD",
28 [BTF_KIND_TYPEDEF] = "TYPEDEF",
29 [BTF_KIND_VOLATILE] = "VOLATILE",
30 [BTF_KIND_CONST] = "CONST",
31 [BTF_KIND_RESTRICT] = "RESTRICT",
32 [BTF_KIND_FUNC] = "FUNC",
33 [BTF_KIND_FUNC_PROTO] = "FUNC_PROTO",
34 [BTF_KIND_VAR] = "VAR",
35 [BTF_KIND_DATASEC] = "DATASEC",
36};
37
38static const char *btf_int_enc_str(__u8 encoding)
39{
40 switch (encoding) {
41 case 0:
42 return "(none)";
43 case BTF_INT_SIGNED:
44 return "SIGNED";
45 case BTF_INT_CHAR:
46 return "CHAR";
47 case BTF_INT_BOOL:
48 return "BOOL";
49 default:
50 return "UNKN";
51 }
52}
53
54static const char *btf_var_linkage_str(__u32 linkage)
55{
56 switch (linkage) {
57 case BTF_VAR_STATIC:
58 return "static";
59 case BTF_VAR_GLOBAL_ALLOCATED:
60 return "global-alloc";
61 default:
62 return "(unknown)";
63 }
64}
65
66static const char *btf_str(const struct btf *btf, __u32 off)
67{
68 if (!off)
69 return "(anon)";
70 return btf__name_by_offset(btf, off) ? : "(invalid)";
71}
72
73static int dump_btf_type(const struct btf *btf, __u32 id,
74 const struct btf_type *t)
75{
76 json_writer_t *w = json_wtr;
77 int kind, safe_kind;
78
79 kind = BTF_INFO_KIND(t->info);
80 safe_kind = kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN;
81
82 if (json_output) {
83 jsonw_start_object(w);
84 jsonw_uint_field(w, "id", id);
85 jsonw_string_field(w, "kind", btf_kind_str[safe_kind]);
86 jsonw_string_field(w, "name", btf_str(btf, t->name_off));
87 } else {
88 printf("[%u] %s '%s'", id, btf_kind_str[safe_kind],
89 btf_str(btf, t->name_off));
90 }
91
92 switch (BTF_INFO_KIND(t->info)) {
93 case BTF_KIND_INT: {
94 __u32 v = *(__u32 *)(t + 1);
95 const char *enc;
96
97 enc = btf_int_enc_str(BTF_INT_ENCODING(v));
98
99 if (json_output) {
100 jsonw_uint_field(w, "size", t->size);
101 jsonw_uint_field(w, "bits_offset", BTF_INT_OFFSET(v));
102 jsonw_uint_field(w, "nr_bits", BTF_INT_BITS(v));
103 jsonw_string_field(w, "encoding", enc);
104 } else {
105 printf(" size=%u bits_offset=%u nr_bits=%u encoding=%s",
106 t->size, BTF_INT_OFFSET(v), BTF_INT_BITS(v),
107 enc);
108 }
109 break;
110 }
111 case BTF_KIND_PTR:
112 case BTF_KIND_CONST:
113 case BTF_KIND_VOLATILE:
114 case BTF_KIND_RESTRICT:
115 case BTF_KIND_TYPEDEF:
116 if (json_output)
117 jsonw_uint_field(w, "type_id", t->type);
118 else
119 printf(" type_id=%u", t->type);
120 break;
121 case BTF_KIND_ARRAY: {
122 const struct btf_array *arr = (const void *)(t + 1);
123
124 if (json_output) {
125 jsonw_uint_field(w, "type_id", arr->type);
126 jsonw_uint_field(w, "index_type_id", arr->index_type);
127 jsonw_uint_field(w, "nr_elems", arr->nelems);
128 } else {
129 printf(" type_id=%u index_type_id=%u nr_elems=%u",
130 arr->type, arr->index_type, arr->nelems);
131 }
132 break;
133 }
134 case BTF_KIND_STRUCT:
135 case BTF_KIND_UNION: {
136 const struct btf_member *m = (const void *)(t + 1);
137 __u16 vlen = BTF_INFO_VLEN(t->info);
138 int i;
139
140 if (json_output) {
141 jsonw_uint_field(w, "size", t->size);
142 jsonw_uint_field(w, "vlen", vlen);
143 jsonw_name(w, "members");
144 jsonw_start_array(w);
145 } else {
146 printf(" size=%u vlen=%u", t->size, vlen);
147 }
148 for (i = 0; i < vlen; i++, m++) {
149 const char *name = btf_str(btf, m->name_off);
150 __u32 bit_off, bit_sz;
151
152 if (BTF_INFO_KFLAG(t->info)) {
153 bit_off = BTF_MEMBER_BIT_OFFSET(m->offset);
154 bit_sz = BTF_MEMBER_BITFIELD_SIZE(m->offset);
155 } else {
156 bit_off = m->offset;
157 bit_sz = 0;
158 }
159
160 if (json_output) {
161 jsonw_start_object(w);
162 jsonw_string_field(w, "name", name);
163 jsonw_uint_field(w, "type_id", m->type);
164 jsonw_uint_field(w, "bits_offset", bit_off);
165 if (bit_sz) {
166 jsonw_uint_field(w, "bitfield_size",
167 bit_sz);
168 }
169 jsonw_end_object(w);
170 } else {
171 printf("\n\t'%s' type_id=%u bits_offset=%u",
172 name, m->type, bit_off);
173 if (bit_sz)
174 printf(" bitfield_size=%u", bit_sz);
175 }
176 }
177 if (json_output)
178 jsonw_end_array(w);
179 break;
180 }
181 case BTF_KIND_ENUM: {
182 const struct btf_enum *v = (const void *)(t + 1);
183 __u16 vlen = BTF_INFO_VLEN(t->info);
184 int i;
185
186 if (json_output) {
187 jsonw_uint_field(w, "size", t->size);
188 jsonw_uint_field(w, "vlen", vlen);
189 jsonw_name(w, "values");
190 jsonw_start_array(w);
191 } else {
192 printf(" size=%u vlen=%u", t->size, vlen);
193 }
194 for (i = 0; i < vlen; i++, v++) {
195 const char *name = btf_str(btf, v->name_off);
196
197 if (json_output) {
198 jsonw_start_object(w);
199 jsonw_string_field(w, "name", name);
200 jsonw_uint_field(w, "val", v->val);
201 jsonw_end_object(w);
202 } else {
203 printf("\n\t'%s' val=%u", name, v->val);
204 }
205 }
206 if (json_output)
207 jsonw_end_array(w);
208 break;
209 }
210 case BTF_KIND_FWD: {
211 const char *fwd_kind = BTF_INFO_KFLAG(t->info) ? "union"
212 : "struct";
213
214 if (json_output)
215 jsonw_string_field(w, "fwd_kind", fwd_kind);
216 else
217 printf(" fwd_kind=%s", fwd_kind);
218 break;
219 }
220 case BTF_KIND_FUNC:
221 if (json_output)
222 jsonw_uint_field(w, "type_id", t->type);
223 else
224 printf(" type_id=%u", t->type);
225 break;
226 case BTF_KIND_FUNC_PROTO: {
227 const struct btf_param *p = (const void *)(t + 1);
228 __u16 vlen = BTF_INFO_VLEN(t->info);
229 int i;
230
231 if (json_output) {
232 jsonw_uint_field(w, "ret_type_id", t->type);
233 jsonw_uint_field(w, "vlen", vlen);
234 jsonw_name(w, "params");
235 jsonw_start_array(w);
236 } else {
237 printf(" ret_type_id=%u vlen=%u", t->type, vlen);
238 }
239 for (i = 0; i < vlen; i++, p++) {
240 const char *name = btf_str(btf, p->name_off);
241
242 if (json_output) {
243 jsonw_start_object(w);
244 jsonw_string_field(w, "name", name);
245 jsonw_uint_field(w, "type_id", p->type);
246 jsonw_end_object(w);
247 } else {
248 printf("\n\t'%s' type_id=%u", name, p->type);
249 }
250 }
251 if (json_output)
252 jsonw_end_array(w);
253 break;
254 }
255 case BTF_KIND_VAR: {
256 const struct btf_var *v = (const void *)(t + 1);
257 const char *linkage;
258
259 linkage = btf_var_linkage_str(v->linkage);
260
261 if (json_output) {
262 jsonw_uint_field(w, "type_id", t->type);
263 jsonw_string_field(w, "linkage", linkage);
264 } else {
265 printf(" type_id=%u, linkage=%s", t->type, linkage);
266 }
267 break;
268 }
269 case BTF_KIND_DATASEC: {
270 const struct btf_var_secinfo *v = (const void *)(t+1);
271 __u16 vlen = BTF_INFO_VLEN(t->info);
272 int i;
273
274 if (json_output) {
275 jsonw_uint_field(w, "size", t->size);
276 jsonw_uint_field(w, "vlen", vlen);
277 jsonw_name(w, "vars");
278 jsonw_start_array(w);
279 } else {
280 printf(" size=%u vlen=%u", t->size, vlen);
281 }
282 for (i = 0; i < vlen; i++, v++) {
283 if (json_output) {
284 jsonw_start_object(w);
285 jsonw_uint_field(w, "type_id", v->type);
286 jsonw_uint_field(w, "offset", v->offset);
287 jsonw_uint_field(w, "size", v->size);
288 jsonw_end_object(w);
289 } else {
290 printf("\n\ttype_id=%u offset=%u size=%u",
291 v->type, v->offset, v->size);
292 }
293 }
294 if (json_output)
295 jsonw_end_array(w);
296 break;
297 }
298 default:
299 break;
300 }
301
302 if (json_output)
303 jsonw_end_object(json_wtr);
304 else
305 printf("\n");
306
307 return 0;
308}
309
310static int dump_btf_raw(const struct btf *btf,
311 __u32 *root_type_ids, int root_type_cnt)
312{
313 const struct btf_type *t;
314 int i;
315
316 if (json_output) {
317 jsonw_start_object(json_wtr);
318 jsonw_name(json_wtr, "types");
319 jsonw_start_array(json_wtr);
320 }
321
322 if (root_type_cnt) {
323 for (i = 0; i < root_type_cnt; i++) {
324 t = btf__type_by_id(btf, root_type_ids[i]);
325 dump_btf_type(btf, root_type_ids[i], t);
326 }
327 } else {
328 int cnt = btf__get_nr_types(btf);
329
330 for (i = 1; i <= cnt; i++) {
331 t = btf__type_by_id(btf, i);
332 dump_btf_type(btf, i, t);
333 }
334 }
335
336 if (json_output) {
337 jsonw_end_array(json_wtr);
338 jsonw_end_object(json_wtr);
339 }
340 return 0;
341}
342
343static void __printf(2, 0) btf_dump_printf(void *ctx,
344 const char *fmt, va_list args)
345{
346 vfprintf(stdout, fmt, args);
347}
348
349static int dump_btf_c(const struct btf *btf,
350 __u32 *root_type_ids, int root_type_cnt)
351{
352 struct btf_dump *d;
353 int err = 0, i;
354
355 d = btf_dump__new(btf, NULL, NULL, btf_dump_printf);
356 if (IS_ERR(d))
357 return PTR_ERR(d);
358
359 if (root_type_cnt) {
360 for (i = 0; i < root_type_cnt; i++) {
361 err = btf_dump__dump_type(d, root_type_ids[i]);
362 if (err)
363 goto done;
364 }
365 } else {
366 int cnt = btf__get_nr_types(btf);
367
368 for (i = 1; i <= cnt; i++) {
369 err = btf_dump__dump_type(d, i);
370 if (err)
371 goto done;
372 }
373 }
374
375done:
376 btf_dump__free(d);
377 return err;
378}
379
380static int do_dump(int argc, char **argv)
381{
382 struct btf *btf = NULL;
383 __u32 root_type_ids[2];
384 int root_type_cnt = 0;
385 bool dump_c = false;
386 __u32 btf_id = -1;
387 const char *src;
388 int fd = -1;
389 int err;
390
391 if (!REQ_ARGS(2)) {
392 usage();
393 return -1;
394 }
395 src = GET_ARG();
396
397 if (is_prefix(src, "map")) {
398 struct bpf_map_info info = {};
399 __u32 len = sizeof(info);
400
401 if (!REQ_ARGS(2)) {
402 usage();
403 return -1;
404 }
405
406 fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
407 if (fd < 0)
408 return -1;
409
410 btf_id = info.btf_id;
411 if (argc && is_prefix(*argv, "key")) {
412 root_type_ids[root_type_cnt++] = info.btf_key_type_id;
413 NEXT_ARG();
414 } else if (argc && is_prefix(*argv, "value")) {
415 root_type_ids[root_type_cnt++] = info.btf_value_type_id;
416 NEXT_ARG();
417 } else if (argc && is_prefix(*argv, "all")) {
418 NEXT_ARG();
419 } else if (argc && is_prefix(*argv, "kv")) {
420 root_type_ids[root_type_cnt++] = info.btf_key_type_id;
421 root_type_ids[root_type_cnt++] = info.btf_value_type_id;
422 NEXT_ARG();
423 } else {
424 root_type_ids[root_type_cnt++] = info.btf_key_type_id;
425 root_type_ids[root_type_cnt++] = info.btf_value_type_id;
426 }
427 } else if (is_prefix(src, "prog")) {
428 struct bpf_prog_info info = {};
429 __u32 len = sizeof(info);
430
431 if (!REQ_ARGS(2)) {
432 usage();
433 return -1;
434 }
435
436 fd = prog_parse_fd(&argc, &argv);
437 if (fd < 0)
438 return -1;
439
440 err = bpf_obj_get_info_by_fd(fd, &info, &len);
441 if (err) {
442 p_err("can't get prog info: %s", strerror(errno));
443 goto done;
444 }
445
446 btf_id = info.btf_id;
447 } else if (is_prefix(src, "id")) {
448 char *endptr;
449
450 btf_id = strtoul(*argv, &endptr, 0);
451 if (*endptr) {
452 p_err("can't parse %s as ID", **argv);
453 return -1;
454 }
455 NEXT_ARG();
456 } else if (is_prefix(src, "file")) {
457 btf = btf__parse_elf(*argv, NULL);
458 if (IS_ERR(btf)) {
459 err = PTR_ERR(btf);
460 btf = NULL;
461 p_err("failed to load BTF from %s: %s",
462 *argv, strerror(err));
463 goto done;
464 }
465 NEXT_ARG();
466 } else {
467 err = -1;
468 p_err("unrecognized BTF source specifier: '%s'", src);
469 goto done;
470 }
471
472 while (argc) {
473 if (is_prefix(*argv, "format")) {
474 NEXT_ARG();
475 if (argc < 1) {
476 p_err("expecting value for 'format' option\n");
477 goto done;
478 }
479 if (strcmp(*argv, "c") == 0) {
480 dump_c = true;
481 } else if (strcmp(*argv, "raw") == 0) {
482 dump_c = false;
483 } else {
484 p_err("unrecognized format specifier: '%s', possible values: raw, c",
485 *argv);
486 goto done;
487 }
488 NEXT_ARG();
489 } else {
490 p_err("unrecognized option: '%s'", *argv);
491 goto done;
492 }
493 }
494
495 if (!btf) {
496 err = btf__get_from_id(btf_id, &btf);
497 if (err) {
498 p_err("get btf by id (%u): %s", btf_id, strerror(err));
499 goto done;
500 }
501 if (!btf) {
502 err = ENOENT;
503 p_err("can't find btf with ID (%u)", btf_id);
504 goto done;
505 }
506 }
507
508 if (dump_c) {
509 if (json_output) {
510 p_err("JSON output for C-syntax dump is not supported");
511 err = -ENOTSUP;
512 goto done;
513 }
514 err = dump_btf_c(btf, root_type_ids, root_type_cnt);
515 } else {
516 err = dump_btf_raw(btf, root_type_ids, root_type_cnt);
517 }
518
519done:
520 close(fd);
521 btf__free(btf);
522 return err;
523}
524
525static int do_help(int argc, char **argv)
526{
527 if (json_output) {
528 jsonw_null(json_wtr);
529 return 0;
530 }
531
532 fprintf(stderr,
533 "Usage: %s btf dump BTF_SRC [format FORMAT]\n"
534 " %s btf help\n"
535 "\n"
536 " BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
537 " FORMAT := { raw | c }\n"
538 " " HELP_SPEC_MAP "\n"
539 " " HELP_SPEC_PROGRAM "\n"
540 " " HELP_SPEC_OPTIONS "\n"
541 "",
542 bin_name, bin_name);
543
544 return 0;
545}
546
547static const struct cmd cmds[] = {
548 { "help", do_help },
549 { "dump", do_dump },
550 { 0 }
551};
552
553int do_btf(int argc, char **argv)
554{
555 return cmd_select(cmds, argc, argv, do_help);
556}
557