blob: 7317438ecd9ec237345ea316de19e52739347001 [file] [log] [blame]
Andrii Nakryikoc93cc692019-04-25 15:30:08 -07001// 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 <gelf.h>
12#include <bpf.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: {
Andrii Nakryiko9c3ddee2019-05-16 23:21:29 -0700211 const char *fwd_kind = BTF_INFO_KFLAG(t->info) ? "union"
212 : "struct";
Andrii Nakryikoc93cc692019-04-25 15:30:08 -0700213
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 bool check_btf_endianness(GElf_Ehdr *ehdr)
344{
345 static unsigned int const endian = 1;
346
347 switch (ehdr->e_ident[EI_DATA]) {
348 case ELFDATA2LSB:
349 return *(unsigned char const *)&endian == 1;
350 case ELFDATA2MSB:
351 return *(unsigned char const *)&endian == 0;
352 default:
353 return 0;
354 }
355}
356
357static int btf_load_from_elf(const char *path, struct btf **btf)
358{
359 int err = -1, fd = -1, idx = 0;
360 Elf_Data *btf_data = NULL;
361 Elf_Scn *scn = NULL;
362 Elf *elf = NULL;
363 GElf_Ehdr ehdr;
364
365 if (elf_version(EV_CURRENT) == EV_NONE) {
366 p_err("failed to init libelf for %s", path);
367 return -1;
368 }
369
370 fd = open(path, O_RDONLY);
371 if (fd < 0) {
372 p_err("failed to open %s: %s", path, strerror(errno));
373 return -1;
374 }
375
376 elf = elf_begin(fd, ELF_C_READ, NULL);
377 if (!elf) {
378 p_err("failed to open %s as ELF file", path);
379 goto done;
380 }
381 if (!gelf_getehdr(elf, &ehdr)) {
382 p_err("failed to get EHDR from %s", path);
383 goto done;
384 }
385 if (!check_btf_endianness(&ehdr)) {
386 p_err("non-native ELF endianness is not supported");
387 goto done;
388 }
389 if (!elf_rawdata(elf_getscn(elf, ehdr.e_shstrndx), NULL)) {
390 p_err("failed to get e_shstrndx from %s\n", path);
391 goto done;
392 }
393
394 while ((scn = elf_nextscn(elf, scn)) != NULL) {
395 GElf_Shdr sh;
396 char *name;
397
398 idx++;
399 if (gelf_getshdr(scn, &sh) != &sh) {
400 p_err("failed to get section(%d) header from %s",
401 idx, path);
402 goto done;
403 }
404 name = elf_strptr(elf, ehdr.e_shstrndx, sh.sh_name);
405 if (!name) {
406 p_err("failed to get section(%d) name from %s",
407 idx, path);
408 goto done;
409 }
410 if (strcmp(name, BTF_ELF_SEC) == 0) {
411 btf_data = elf_getdata(scn, 0);
412 if (!btf_data) {
413 p_err("failed to get section(%d, %s) data from %s",
414 idx, name, path);
415 goto done;
416 }
417 break;
418 }
419 }
420
421 if (!btf_data) {
422 p_err("%s ELF section not found in %s", BTF_ELF_SEC, path);
423 goto done;
424 }
425
426 *btf = btf__new(btf_data->d_buf, btf_data->d_size);
427 if (IS_ERR(*btf)) {
428 err = PTR_ERR(*btf);
429 *btf = NULL;
430 p_err("failed to load BTF data from %s: %s",
431 path, strerror(err));
432 goto done;
433 }
434
435 err = 0;
436done:
437 if (err) {
438 if (*btf) {
439 btf__free(*btf);
440 *btf = NULL;
441 }
442 }
443 if (elf)
444 elf_end(elf);
445 close(fd);
446 return err;
447}
448
449static int do_dump(int argc, char **argv)
450{
451 struct btf *btf = NULL;
452 __u32 root_type_ids[2];
453 int root_type_cnt = 0;
454 __u32 btf_id = -1;
455 const char *src;
456 int fd = -1;
457 int err;
458
459 if (!REQ_ARGS(2)) {
460 usage();
461 return -1;
462 }
463 src = GET_ARG();
464
465 if (is_prefix(src, "map")) {
466 struct bpf_map_info info = {};
467 __u32 len = sizeof(info);
468
469 if (!REQ_ARGS(2)) {
470 usage();
471 return -1;
472 }
473
474 fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
475 if (fd < 0)
476 return -1;
477
478 btf_id = info.btf_id;
479 if (argc && is_prefix(*argv, "key")) {
480 root_type_ids[root_type_cnt++] = info.btf_key_type_id;
481 NEXT_ARG();
482 } else if (argc && is_prefix(*argv, "value")) {
483 root_type_ids[root_type_cnt++] = info.btf_value_type_id;
484 NEXT_ARG();
485 } else if (argc && is_prefix(*argv, "all")) {
486 NEXT_ARG();
487 } else if (argc && is_prefix(*argv, "kv")) {
488 root_type_ids[root_type_cnt++] = info.btf_key_type_id;
489 root_type_ids[root_type_cnt++] = info.btf_value_type_id;
490 NEXT_ARG();
491 } else {
492 root_type_ids[root_type_cnt++] = info.btf_key_type_id;
493 root_type_ids[root_type_cnt++] = info.btf_value_type_id;
494 }
495 } else if (is_prefix(src, "prog")) {
496 struct bpf_prog_info info = {};
497 __u32 len = sizeof(info);
498
499 if (!REQ_ARGS(2)) {
500 usage();
501 return -1;
502 }
503
504 fd = prog_parse_fd(&argc, &argv);
505 if (fd < 0)
506 return -1;
507
508 err = bpf_obj_get_info_by_fd(fd, &info, &len);
509 if (err) {
510 p_err("can't get prog info: %s", strerror(errno));
511 goto done;
512 }
513
514 btf_id = info.btf_id;
515 } else if (is_prefix(src, "id")) {
516 char *endptr;
517
518 btf_id = strtoul(*argv, &endptr, 0);
519 if (*endptr) {
520 p_err("can't parse %s as ID", **argv);
521 return -1;
522 }
523 NEXT_ARG();
524 } else if (is_prefix(src, "file")) {
525 err = btf_load_from_elf(*argv, &btf);
526 if (err)
527 goto done;
528 NEXT_ARG();
529 } else {
530 err = -1;
531 p_err("unrecognized BTF source specifier: '%s'", src);
532 goto done;
533 }
534
535 if (!btf) {
536 err = btf__get_from_id(btf_id, &btf);
537 if (err) {
538 p_err("get btf by id (%u): %s", btf_id, strerror(err));
539 goto done;
540 }
541 if (!btf) {
542 err = ENOENT;
543 p_err("can't find btf with ID (%u)", btf_id);
544 goto done;
545 }
546 }
547
548 dump_btf_raw(btf, root_type_ids, root_type_cnt);
549
550done:
551 close(fd);
552 btf__free(btf);
553 return err;
554}
555
556static int do_help(int argc, char **argv)
557{
558 if (json_output) {
559 jsonw_null(json_wtr);
560 return 0;
561 }
562
563 fprintf(stderr,
564 "Usage: %s btf dump BTF_SRC\n"
565 " %s btf help\n"
566 "\n"
567 " BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
568 " " HELP_SPEC_MAP "\n"
569 " " HELP_SPEC_PROGRAM "\n"
570 " " HELP_SPEC_OPTIONS "\n"
571 "",
572 bin_name, bin_name);
573
574 return 0;
575}
576
577static const struct cmd cmds[] = {
578 { "help", do_help },
579 { "dump", do_dump },
580 { 0 }
581};
582
583int do_btf(int argc, char **argv)
584{
585 return cmd_select(cmds, argc, argv, do_help);
586}