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 <linux/btf.h> |
12 | #include <sys/types.h> |
13 | #include <sys/stat.h> |
14 | |
15 | #include <bpf/bpf.h> |
16 | #include <bpf/btf.h> |
17 | #include <bpf/hashmap.h> |
18 | #include <bpf/libbpf.h> |
19 | |
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 | [BTF_KIND_FLOAT] = "FLOAT" , |
41 | [BTF_KIND_DECL_TAG] = "DECL_TAG" , |
42 | [BTF_KIND_TYPE_TAG] = "TYPE_TAG" , |
43 | [BTF_KIND_ENUM64] = "ENUM64" , |
44 | }; |
45 | |
46 | static const char *btf_int_enc_str(__u8 encoding) |
47 | { |
48 | switch (encoding) { |
49 | case 0: |
50 | return "(none)" ; |
51 | case BTF_INT_SIGNED: |
52 | return "SIGNED" ; |
53 | case BTF_INT_CHAR: |
54 | return "CHAR" ; |
55 | case BTF_INT_BOOL: |
56 | return "BOOL" ; |
57 | default: |
58 | return "UNKN" ; |
59 | } |
60 | } |
61 | |
62 | static const char *btf_var_linkage_str(__u32 linkage) |
63 | { |
64 | switch (linkage) { |
65 | case BTF_VAR_STATIC: |
66 | return "static" ; |
67 | case BTF_VAR_GLOBAL_ALLOCATED: |
68 | return "global" ; |
69 | case BTF_VAR_GLOBAL_EXTERN: |
70 | return "extern" ; |
71 | default: |
72 | return "(unknown)" ; |
73 | } |
74 | } |
75 | |
76 | static const char *btf_func_linkage_str(const struct btf_type *t) |
77 | { |
78 | switch (btf_vlen(t)) { |
79 | case BTF_FUNC_STATIC: |
80 | return "static" ; |
81 | case BTF_FUNC_GLOBAL: |
82 | return "global" ; |
83 | case BTF_FUNC_EXTERN: |
84 | return "extern" ; |
85 | default: |
86 | return "(unknown)" ; |
87 | } |
88 | } |
89 | |
90 | static const char *btf_str(const struct btf *btf, __u32 off) |
91 | { |
92 | if (!off) |
93 | return "(anon)" ; |
94 | return btf__name_by_offset(btf, off) ? : "(invalid)" ; |
95 | } |
96 | |
97 | static int btf_kind_safe(int kind) |
98 | { |
99 | return kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN; |
100 | } |
101 | |
102 | static int dump_btf_type(const struct btf *btf, __u32 id, |
103 | const struct btf_type *t) |
104 | { |
105 | json_writer_t *w = json_wtr; |
106 | int kind = btf_kind(t); |
107 | |
108 | if (json_output) { |
109 | jsonw_start_object(self: w); |
110 | jsonw_uint_field(self: w, prop: "id" , num: id); |
111 | jsonw_string_field(self: w, prop: "kind" , val: btf_kind_str[btf_kind_safe(kind)]); |
112 | jsonw_string_field(self: w, prop: "name" , val: btf_str(btf, off: t->name_off)); |
113 | } else { |
114 | printf("[%u] %s '%s'" , id, btf_kind_str[btf_kind_safe(kind)], |
115 | btf_str(btf, off: t->name_off)); |
116 | } |
117 | |
118 | switch (kind) { |
119 | case BTF_KIND_INT: { |
120 | __u32 v = *(__u32 *)(t + 1); |
121 | const char *enc; |
122 | |
123 | enc = btf_int_enc_str(BTF_INT_ENCODING(v)); |
124 | |
125 | if (json_output) { |
126 | jsonw_uint_field(self: w, prop: "size" , num: t->size); |
127 | jsonw_uint_field(self: w, prop: "bits_offset" , BTF_INT_OFFSET(v)); |
128 | jsonw_uint_field(self: w, prop: "nr_bits" , BTF_INT_BITS(v)); |
129 | jsonw_string_field(self: w, prop: "encoding" , val: enc); |
130 | } else { |
131 | printf(" size=%u bits_offset=%u nr_bits=%u encoding=%s" , |
132 | t->size, BTF_INT_OFFSET(v), BTF_INT_BITS(v), |
133 | enc); |
134 | } |
135 | break; |
136 | } |
137 | case BTF_KIND_PTR: |
138 | case BTF_KIND_CONST: |
139 | case BTF_KIND_VOLATILE: |
140 | case BTF_KIND_RESTRICT: |
141 | case BTF_KIND_TYPEDEF: |
142 | case BTF_KIND_TYPE_TAG: |
143 | if (json_output) |
144 | jsonw_uint_field(self: w, prop: "type_id" , num: t->type); |
145 | else |
146 | printf(" type_id=%u" , t->type); |
147 | break; |
148 | case BTF_KIND_ARRAY: { |
149 | const struct btf_array *arr = (const void *)(t + 1); |
150 | |
151 | if (json_output) { |
152 | jsonw_uint_field(self: w, prop: "type_id" , num: arr->type); |
153 | jsonw_uint_field(self: w, prop: "index_type_id" , num: arr->index_type); |
154 | jsonw_uint_field(self: w, prop: "nr_elems" , num: arr->nelems); |
155 | } else { |
156 | printf(" type_id=%u index_type_id=%u nr_elems=%u" , |
157 | arr->type, arr->index_type, arr->nelems); |
158 | } |
159 | break; |
160 | } |
161 | case BTF_KIND_STRUCT: |
162 | case BTF_KIND_UNION: { |
163 | const struct btf_member *m = (const void *)(t + 1); |
164 | __u16 vlen = BTF_INFO_VLEN(t->info); |
165 | int i; |
166 | |
167 | if (json_output) { |
168 | jsonw_uint_field(self: w, prop: "size" , num: t->size); |
169 | jsonw_uint_field(self: w, prop: "vlen" , num: vlen); |
170 | jsonw_name(self: w, name: "members" ); |
171 | jsonw_start_array(self: w); |
172 | } else { |
173 | printf(" size=%u vlen=%u" , t->size, vlen); |
174 | } |
175 | for (i = 0; i < vlen; i++, m++) { |
176 | const char *name = btf_str(btf, off: m->name_off); |
177 | __u32 bit_off, bit_sz; |
178 | |
179 | if (BTF_INFO_KFLAG(t->info)) { |
180 | bit_off = BTF_MEMBER_BIT_OFFSET(m->offset); |
181 | bit_sz = BTF_MEMBER_BITFIELD_SIZE(m->offset); |
182 | } else { |
183 | bit_off = m->offset; |
184 | bit_sz = 0; |
185 | } |
186 | |
187 | if (json_output) { |
188 | jsonw_start_object(self: w); |
189 | jsonw_string_field(self: w, prop: "name" , val: name); |
190 | jsonw_uint_field(self: w, prop: "type_id" , num: m->type); |
191 | jsonw_uint_field(self: w, prop: "bits_offset" , num: bit_off); |
192 | if (bit_sz) { |
193 | jsonw_uint_field(self: w, prop: "bitfield_size" , |
194 | num: bit_sz); |
195 | } |
196 | jsonw_end_object(self: w); |
197 | } else { |
198 | printf("\n\t'%s' type_id=%u bits_offset=%u" , |
199 | name, m->type, bit_off); |
200 | if (bit_sz) |
201 | printf(" bitfield_size=%u" , bit_sz); |
202 | } |
203 | } |
204 | if (json_output) |
205 | jsonw_end_array(self: w); |
206 | break; |
207 | } |
208 | case BTF_KIND_ENUM: { |
209 | const struct btf_enum *v = (const void *)(t + 1); |
210 | __u16 vlen = BTF_INFO_VLEN(t->info); |
211 | const char *encoding; |
212 | int i; |
213 | |
214 | encoding = btf_kflag(t) ? "SIGNED" : "UNSIGNED" ; |
215 | if (json_output) { |
216 | jsonw_string_field(self: w, prop: "encoding" , val: encoding); |
217 | jsonw_uint_field(self: w, prop: "size" , num: t->size); |
218 | jsonw_uint_field(self: w, prop: "vlen" , num: vlen); |
219 | jsonw_name(self: w, name: "values" ); |
220 | jsonw_start_array(self: w); |
221 | } else { |
222 | printf(" encoding=%s size=%u vlen=%u" , encoding, t->size, vlen); |
223 | } |
224 | for (i = 0; i < vlen; i++, v++) { |
225 | const char *name = btf_str(btf, off: v->name_off); |
226 | |
227 | if (json_output) { |
228 | jsonw_start_object(self: w); |
229 | jsonw_string_field(self: w, prop: "name" , val: name); |
230 | if (btf_kflag(t)) |
231 | jsonw_int_field(self: w, prop: "val" , num: v->val); |
232 | else |
233 | jsonw_uint_field(self: w, prop: "val" , num: v->val); |
234 | jsonw_end_object(self: w); |
235 | } else { |
236 | if (btf_kflag(t)) |
237 | printf("\n\t'%s' val=%d" , name, v->val); |
238 | else |
239 | printf("\n\t'%s' val=%u" , name, v->val); |
240 | } |
241 | } |
242 | if (json_output) |
243 | jsonw_end_array(self: w); |
244 | break; |
245 | } |
246 | case BTF_KIND_ENUM64: { |
247 | const struct btf_enum64 *v = btf_enum64(t); |
248 | __u16 vlen = btf_vlen(t); |
249 | const char *encoding; |
250 | int i; |
251 | |
252 | encoding = btf_kflag(t) ? "SIGNED" : "UNSIGNED" ; |
253 | if (json_output) { |
254 | jsonw_string_field(self: w, prop: "encoding" , val: encoding); |
255 | jsonw_uint_field(self: w, prop: "size" , num: t->size); |
256 | jsonw_uint_field(self: w, prop: "vlen" , num: vlen); |
257 | jsonw_name(self: w, name: "values" ); |
258 | jsonw_start_array(self: w); |
259 | } else { |
260 | printf(" encoding=%s size=%u vlen=%u" , encoding, t->size, vlen); |
261 | } |
262 | for (i = 0; i < vlen; i++, v++) { |
263 | const char *name = btf_str(btf, off: v->name_off); |
264 | __u64 val = ((__u64)v->val_hi32 << 32) | v->val_lo32; |
265 | |
266 | if (json_output) { |
267 | jsonw_start_object(self: w); |
268 | jsonw_string_field(self: w, prop: "name" , val: name); |
269 | if (btf_kflag(t)) |
270 | jsonw_int_field(self: w, prop: "val" , num: val); |
271 | else |
272 | jsonw_uint_field(self: w, prop: "val" , num: val); |
273 | jsonw_end_object(self: w); |
274 | } else { |
275 | if (btf_kflag(t)) |
276 | printf("\n\t'%s' val=%lldLL" , name, |
277 | (unsigned long long)val); |
278 | else |
279 | printf("\n\t'%s' val=%lluULL" , name, |
280 | (unsigned long long)val); |
281 | } |
282 | } |
283 | if (json_output) |
284 | jsonw_end_array(self: w); |
285 | break; |
286 | } |
287 | case BTF_KIND_FWD: { |
288 | const char *fwd_kind = BTF_INFO_KFLAG(t->info) ? "union" |
289 | : "struct" ; |
290 | |
291 | if (json_output) |
292 | jsonw_string_field(self: w, prop: "fwd_kind" , val: fwd_kind); |
293 | else |
294 | printf(" fwd_kind=%s" , fwd_kind); |
295 | break; |
296 | } |
297 | case BTF_KIND_FUNC: { |
298 | const char *linkage = btf_func_linkage_str(t); |
299 | |
300 | if (json_output) { |
301 | jsonw_uint_field(self: w, prop: "type_id" , num: t->type); |
302 | jsonw_string_field(self: w, prop: "linkage" , val: linkage); |
303 | } else { |
304 | printf(" type_id=%u linkage=%s" , t->type, linkage); |
305 | } |
306 | break; |
307 | } |
308 | case BTF_KIND_FUNC_PROTO: { |
309 | const struct btf_param *p = (const void *)(t + 1); |
310 | __u16 vlen = BTF_INFO_VLEN(t->info); |
311 | int i; |
312 | |
313 | if (json_output) { |
314 | jsonw_uint_field(self: w, prop: "ret_type_id" , num: t->type); |
315 | jsonw_uint_field(self: w, prop: "vlen" , num: vlen); |
316 | jsonw_name(self: w, name: "params" ); |
317 | jsonw_start_array(self: w); |
318 | } else { |
319 | printf(" ret_type_id=%u vlen=%u" , t->type, vlen); |
320 | } |
321 | for (i = 0; i < vlen; i++, p++) { |
322 | const char *name = btf_str(btf, off: p->name_off); |
323 | |
324 | if (json_output) { |
325 | jsonw_start_object(self: w); |
326 | jsonw_string_field(self: w, prop: "name" , val: name); |
327 | jsonw_uint_field(self: w, prop: "type_id" , num: p->type); |
328 | jsonw_end_object(self: w); |
329 | } else { |
330 | printf("\n\t'%s' type_id=%u" , name, p->type); |
331 | } |
332 | } |
333 | if (json_output) |
334 | jsonw_end_array(self: w); |
335 | break; |
336 | } |
337 | case BTF_KIND_VAR: { |
338 | const struct btf_var *v = (const void *)(t + 1); |
339 | const char *linkage; |
340 | |
341 | linkage = btf_var_linkage_str(linkage: v->linkage); |
342 | |
343 | if (json_output) { |
344 | jsonw_uint_field(self: w, prop: "type_id" , num: t->type); |
345 | jsonw_string_field(self: w, prop: "linkage" , val: linkage); |
346 | } else { |
347 | printf(" type_id=%u, linkage=%s" , t->type, linkage); |
348 | } |
349 | break; |
350 | } |
351 | case BTF_KIND_DATASEC: { |
352 | const struct btf_var_secinfo *v = (const void *)(t + 1); |
353 | const struct btf_type *vt; |
354 | __u16 vlen = BTF_INFO_VLEN(t->info); |
355 | int i; |
356 | |
357 | if (json_output) { |
358 | jsonw_uint_field(self: w, prop: "size" , num: t->size); |
359 | jsonw_uint_field(self: w, prop: "vlen" , num: vlen); |
360 | jsonw_name(self: w, name: "vars" ); |
361 | jsonw_start_array(self: w); |
362 | } else { |
363 | printf(" size=%u vlen=%u" , t->size, vlen); |
364 | } |
365 | for (i = 0; i < vlen; i++, v++) { |
366 | if (json_output) { |
367 | jsonw_start_object(self: w); |
368 | jsonw_uint_field(self: w, prop: "type_id" , num: v->type); |
369 | jsonw_uint_field(self: w, prop: "offset" , num: v->offset); |
370 | jsonw_uint_field(self: w, prop: "size" , num: v->size); |
371 | jsonw_end_object(self: w); |
372 | } else { |
373 | printf("\n\ttype_id=%u offset=%u size=%u" , |
374 | v->type, v->offset, v->size); |
375 | |
376 | if (v->type < btf__type_cnt(btf)) { |
377 | vt = btf__type_by_id(btf, v->type); |
378 | printf(" (%s '%s')" , |
379 | btf_kind_str[btf_kind_safe(kind: btf_kind(t: vt))], |
380 | btf_str(btf, off: vt->name_off)); |
381 | } |
382 | } |
383 | } |
384 | if (json_output) |
385 | jsonw_end_array(self: w); |
386 | break; |
387 | } |
388 | case BTF_KIND_FLOAT: { |
389 | if (json_output) |
390 | jsonw_uint_field(self: w, prop: "size" , num: t->size); |
391 | else |
392 | printf(" size=%u" , t->size); |
393 | break; |
394 | } |
395 | case BTF_KIND_DECL_TAG: { |
396 | const struct btf_decl_tag *tag = (const void *)(t + 1); |
397 | |
398 | if (json_output) { |
399 | jsonw_uint_field(self: w, prop: "type_id" , num: t->type); |
400 | jsonw_int_field(self: w, prop: "component_idx" , num: tag->component_idx); |
401 | } else { |
402 | printf(" type_id=%u component_idx=%d" , t->type, tag->component_idx); |
403 | } |
404 | break; |
405 | } |
406 | default: |
407 | break; |
408 | } |
409 | |
410 | if (json_output) |
411 | jsonw_end_object(self: json_wtr); |
412 | else |
413 | printf("\n" ); |
414 | |
415 | return 0; |
416 | } |
417 | |
418 | static int dump_btf_raw(const struct btf *btf, |
419 | __u32 *root_type_ids, int root_type_cnt) |
420 | { |
421 | const struct btf_type *t; |
422 | int i; |
423 | |
424 | if (json_output) { |
425 | jsonw_start_object(self: json_wtr); |
426 | jsonw_name(self: json_wtr, name: "types" ); |
427 | jsonw_start_array(self: json_wtr); |
428 | } |
429 | |
430 | if (root_type_cnt) { |
431 | for (i = 0; i < root_type_cnt; i++) { |
432 | t = btf__type_by_id(btf, root_type_ids[i]); |
433 | dump_btf_type(btf, id: root_type_ids[i], t); |
434 | } |
435 | } else { |
436 | const struct btf *base; |
437 | int cnt = btf__type_cnt(btf); |
438 | int start_id = 1; |
439 | |
440 | base = btf__base_btf(btf); |
441 | if (base) |
442 | start_id = btf__type_cnt(base); |
443 | |
444 | for (i = start_id; i < cnt; i++) { |
445 | t = btf__type_by_id(btf, i); |
446 | dump_btf_type(btf, id: i, t); |
447 | } |
448 | } |
449 | |
450 | if (json_output) { |
451 | jsonw_end_array(self: json_wtr); |
452 | jsonw_end_object(self: json_wtr); |
453 | } |
454 | return 0; |
455 | } |
456 | |
457 | static void __printf(2, 0) btf_dump_printf(void *ctx, |
458 | const char *fmt, va_list args) |
459 | { |
460 | vfprintf(stdout, fmt, args); |
461 | } |
462 | |
463 | static int dump_btf_c(const struct btf *btf, |
464 | __u32 *root_type_ids, int root_type_cnt) |
465 | { |
466 | struct btf_dump *d; |
467 | int err = 0, i; |
468 | |
469 | d = btf_dump__new(btf, btf_dump_printf, NULL, NULL); |
470 | if (!d) |
471 | return -errno; |
472 | |
473 | printf("#ifndef __VMLINUX_H__\n" ); |
474 | printf("#define __VMLINUX_H__\n" ); |
475 | printf("\n" ); |
476 | printf("#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n" ); |
477 | printf("#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)\n" ); |
478 | printf("#endif\n\n" ); |
479 | |
480 | if (root_type_cnt) { |
481 | for (i = 0; i < root_type_cnt; i++) { |
482 | err = btf_dump__dump_type(d, root_type_ids[i]); |
483 | if (err) |
484 | goto done; |
485 | } |
486 | } else { |
487 | int cnt = btf__type_cnt(btf); |
488 | |
489 | for (i = 1; i < cnt; i++) { |
490 | err = btf_dump__dump_type(d, i); |
491 | if (err) |
492 | goto done; |
493 | } |
494 | } |
495 | |
496 | printf("#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n" ); |
497 | printf("#pragma clang attribute pop\n" ); |
498 | printf("#endif\n" ); |
499 | printf("\n" ); |
500 | printf("#endif /* __VMLINUX_H__ */\n" ); |
501 | |
502 | done: |
503 | btf_dump__free(d); |
504 | return err; |
505 | } |
506 | |
507 | static const char sysfs_vmlinux[] = "/sys/kernel/btf/vmlinux" ; |
508 | |
509 | static struct btf *get_vmlinux_btf_from_sysfs(void) |
510 | { |
511 | struct btf *base; |
512 | |
513 | base = btf__parse(sysfs_vmlinux, NULL); |
514 | if (!base) |
515 | p_err(fmt: "failed to parse vmlinux BTF at '%s': %d\n" , |
516 | sysfs_vmlinux, -errno); |
517 | |
518 | return base; |
519 | } |
520 | |
521 | #define BTF_NAME_BUFF_LEN 64 |
522 | |
523 | static bool btf_is_kernel_module(__u32 btf_id) |
524 | { |
525 | struct bpf_btf_info btf_info = {}; |
526 | char btf_name[BTF_NAME_BUFF_LEN]; |
527 | int btf_fd; |
528 | __u32 len; |
529 | int err; |
530 | |
531 | btf_fd = bpf_btf_get_fd_by_id(btf_id); |
532 | if (btf_fd < 0) { |
533 | p_err(fmt: "can't get BTF object by id (%u): %s" , btf_id, strerror(errno)); |
534 | return false; |
535 | } |
536 | |
537 | len = sizeof(btf_info); |
538 | btf_info.name = ptr_to_u64(ptr: btf_name); |
539 | btf_info.name_len = sizeof(btf_name); |
540 | err = bpf_btf_get_info_by_fd(btf_fd, &btf_info, &len); |
541 | close(btf_fd); |
542 | if (err) { |
543 | p_err(fmt: "can't get BTF (ID %u) object info: %s" , btf_id, strerror(errno)); |
544 | return false; |
545 | } |
546 | |
547 | return btf_info.kernel_btf && strncmp(btf_name, "vmlinux" , sizeof(btf_name)) != 0; |
548 | } |
549 | |
550 | static int do_dump(int argc, char **argv) |
551 | { |
552 | struct btf *btf = NULL, *base = NULL; |
553 | __u32 root_type_ids[2]; |
554 | int root_type_cnt = 0; |
555 | bool dump_c = false; |
556 | __u32 btf_id = -1; |
557 | const char *src; |
558 | int fd = -1; |
559 | int err = 0; |
560 | |
561 | if (!REQ_ARGS(2)) { |
562 | usage(); |
563 | return -1; |
564 | } |
565 | src = GET_ARG(); |
566 | if (is_prefix(pfx: src, str: "map" )) { |
567 | struct bpf_map_info info = {}; |
568 | __u32 len = sizeof(info); |
569 | |
570 | if (!REQ_ARGS(2)) { |
571 | usage(); |
572 | return -1; |
573 | } |
574 | |
575 | fd = map_parse_fd_and_info(argc: &argc, argv: &argv, info: &info, info_len: &len); |
576 | if (fd < 0) |
577 | return -1; |
578 | |
579 | btf_id = info.btf_id; |
580 | if (argc && is_prefix(pfx: *argv, str: "key" )) { |
581 | root_type_ids[root_type_cnt++] = info.btf_key_type_id; |
582 | NEXT_ARG(); |
583 | } else if (argc && is_prefix(pfx: *argv, str: "value" )) { |
584 | root_type_ids[root_type_cnt++] = info.btf_value_type_id; |
585 | NEXT_ARG(); |
586 | } else if (argc && is_prefix(pfx: *argv, str: "all" )) { |
587 | NEXT_ARG(); |
588 | } else if (argc && is_prefix(pfx: *argv, str: "kv" )) { |
589 | root_type_ids[root_type_cnt++] = info.btf_key_type_id; |
590 | root_type_ids[root_type_cnt++] = info.btf_value_type_id; |
591 | NEXT_ARG(); |
592 | } else { |
593 | root_type_ids[root_type_cnt++] = info.btf_key_type_id; |
594 | root_type_ids[root_type_cnt++] = info.btf_value_type_id; |
595 | } |
596 | } else if (is_prefix(pfx: src, str: "prog" )) { |
597 | struct bpf_prog_info info = {}; |
598 | __u32 len = sizeof(info); |
599 | |
600 | if (!REQ_ARGS(2)) { |
601 | usage(); |
602 | return -1; |
603 | } |
604 | |
605 | fd = prog_parse_fd(argc: &argc, argv: &argv); |
606 | if (fd < 0) |
607 | return -1; |
608 | |
609 | err = bpf_prog_get_info_by_fd(fd, &info, &len); |
610 | if (err) { |
611 | p_err(fmt: "can't get prog info: %s" , strerror(errno)); |
612 | goto done; |
613 | } |
614 | |
615 | btf_id = info.btf_id; |
616 | } else if (is_prefix(pfx: src, str: "id" )) { |
617 | char *endptr; |
618 | |
619 | btf_id = strtoul(*argv, &endptr, 0); |
620 | if (*endptr) { |
621 | p_err(fmt: "can't parse %s as ID" , *argv); |
622 | return -1; |
623 | } |
624 | NEXT_ARG(); |
625 | } else if (is_prefix(pfx: src, str: "file" )) { |
626 | const char sysfs_prefix[] = "/sys/kernel/btf/" ; |
627 | |
628 | if (!base_btf && |
629 | strncmp(*argv, sysfs_prefix, sizeof(sysfs_prefix) - 1) == 0 && |
630 | strcmp(*argv, sysfs_vmlinux) != 0) |
631 | base = get_vmlinux_btf_from_sysfs(); |
632 | |
633 | btf = btf__parse_split(*argv, base ?: base_btf); |
634 | if (!btf) { |
635 | err = -errno; |
636 | p_err(fmt: "failed to load BTF from %s: %s" , |
637 | *argv, strerror(errno)); |
638 | goto done; |
639 | } |
640 | NEXT_ARG(); |
641 | } else { |
642 | err = -1; |
643 | p_err(fmt: "unrecognized BTF source specifier: '%s'" , src); |
644 | goto done; |
645 | } |
646 | |
647 | while (argc) { |
648 | if (is_prefix(pfx: *argv, str: "format" )) { |
649 | NEXT_ARG(); |
650 | if (argc < 1) { |
651 | p_err(fmt: "expecting value for 'format' option\n" ); |
652 | err = -EINVAL; |
653 | goto done; |
654 | } |
655 | if (strcmp(*argv, "c" ) == 0) { |
656 | dump_c = true; |
657 | } else if (strcmp(*argv, "raw" ) == 0) { |
658 | dump_c = false; |
659 | } else { |
660 | p_err(fmt: "unrecognized format specifier: '%s', possible values: raw, c" , |
661 | *argv); |
662 | err = -EINVAL; |
663 | goto done; |
664 | } |
665 | NEXT_ARG(); |
666 | } else { |
667 | p_err(fmt: "unrecognized option: '%s'" , *argv); |
668 | err = -EINVAL; |
669 | goto done; |
670 | } |
671 | } |
672 | |
673 | if (!btf) { |
674 | if (!base_btf && btf_is_kernel_module(btf_id)) { |
675 | p_info(fmt: "Warning: valid base BTF was not specified with -B option, falling back to standard base BTF (%s)" , |
676 | sysfs_vmlinux); |
677 | base_btf = get_vmlinux_btf_from_sysfs(); |
678 | } |
679 | |
680 | btf = btf__load_from_kernel_by_id_split(btf_id, base_btf); |
681 | if (!btf) { |
682 | err = -errno; |
683 | p_err(fmt: "get btf by id (%u): %s" , btf_id, strerror(errno)); |
684 | goto done; |
685 | } |
686 | } |
687 | |
688 | if (dump_c) { |
689 | if (json_output) { |
690 | p_err(fmt: "JSON output for C-syntax dump is not supported" ); |
691 | err = -ENOTSUP; |
692 | goto done; |
693 | } |
694 | err = dump_btf_c(btf, root_type_ids, root_type_cnt); |
695 | } else { |
696 | err = dump_btf_raw(btf, root_type_ids, root_type_cnt); |
697 | } |
698 | |
699 | done: |
700 | close(fd); |
701 | btf__free(btf); |
702 | btf__free(base); |
703 | return err; |
704 | } |
705 | |
706 | static int btf_parse_fd(int *argc, char ***argv) |
707 | { |
708 | unsigned int id; |
709 | char *endptr; |
710 | int fd; |
711 | |
712 | if (!is_prefix(pfx: *argv[0], str: "id" )) { |
713 | p_err(fmt: "expected 'id', got: '%s'?" , **argv); |
714 | return -1; |
715 | } |
716 | NEXT_ARGP(); |
717 | |
718 | id = strtoul(**argv, &endptr, 0); |
719 | if (*endptr) { |
720 | p_err(fmt: "can't parse %s as ID" , **argv); |
721 | return -1; |
722 | } |
723 | NEXT_ARGP(); |
724 | |
725 | fd = bpf_btf_get_fd_by_id(id); |
726 | if (fd < 0) |
727 | p_err(fmt: "can't get BTF object by id (%u): %s" , |
728 | id, strerror(errno)); |
729 | |
730 | return fd; |
731 | } |
732 | |
733 | static int |
734 | build_btf_type_table(struct hashmap *tab, enum bpf_obj_type type, |
735 | void *info, __u32 *len) |
736 | { |
737 | static const char * const names[] = { |
738 | [BPF_OBJ_UNKNOWN] = "unknown" , |
739 | [BPF_OBJ_PROG] = "prog" , |
740 | [BPF_OBJ_MAP] = "map" , |
741 | }; |
742 | __u32 btf_id, id = 0; |
743 | int err; |
744 | int fd; |
745 | |
746 | while (true) { |
747 | switch (type) { |
748 | case BPF_OBJ_PROG: |
749 | err = bpf_prog_get_next_id(id, &id); |
750 | break; |
751 | case BPF_OBJ_MAP: |
752 | err = bpf_map_get_next_id(id, &id); |
753 | break; |
754 | default: |
755 | err = -1; |
756 | p_err(fmt: "unexpected object type: %d" , type); |
757 | goto err_free; |
758 | } |
759 | if (err) { |
760 | if (errno == ENOENT) { |
761 | err = 0; |
762 | break; |
763 | } |
764 | p_err(fmt: "can't get next %s: %s%s" , names[type], |
765 | strerror(errno), |
766 | errno == EINVAL ? " -- kernel too old?" : "" ); |
767 | goto err_free; |
768 | } |
769 | |
770 | switch (type) { |
771 | case BPF_OBJ_PROG: |
772 | fd = bpf_prog_get_fd_by_id(id); |
773 | break; |
774 | case BPF_OBJ_MAP: |
775 | fd = bpf_map_get_fd_by_id(id); |
776 | break; |
777 | default: |
778 | err = -1; |
779 | p_err(fmt: "unexpected object type: %d" , type); |
780 | goto err_free; |
781 | } |
782 | if (fd < 0) { |
783 | if (errno == ENOENT) |
784 | continue; |
785 | p_err(fmt: "can't get %s by id (%u): %s" , names[type], id, |
786 | strerror(errno)); |
787 | err = -1; |
788 | goto err_free; |
789 | } |
790 | |
791 | memset(info, 0, *len); |
792 | if (type == BPF_OBJ_PROG) |
793 | err = bpf_prog_get_info_by_fd(fd, info, len); |
794 | else |
795 | err = bpf_map_get_info_by_fd(fd, info, len); |
796 | close(fd); |
797 | if (err) { |
798 | p_err(fmt: "can't get %s info: %s" , names[type], |
799 | strerror(errno)); |
800 | goto err_free; |
801 | } |
802 | |
803 | switch (type) { |
804 | case BPF_OBJ_PROG: |
805 | btf_id = ((struct bpf_prog_info *)info)->btf_id; |
806 | break; |
807 | case BPF_OBJ_MAP: |
808 | btf_id = ((struct bpf_map_info *)info)->btf_id; |
809 | break; |
810 | default: |
811 | err = -1; |
812 | p_err(fmt: "unexpected object type: %d" , type); |
813 | goto err_free; |
814 | } |
815 | if (!btf_id) |
816 | continue; |
817 | |
818 | err = hashmap__append(tab, btf_id, id); |
819 | if (err) { |
820 | p_err(fmt: "failed to append entry to hashmap for BTF ID %u, object ID %u: %s" , |
821 | btf_id, id, strerror(-err)); |
822 | goto err_free; |
823 | } |
824 | } |
825 | |
826 | return 0; |
827 | |
828 | err_free: |
829 | hashmap__free(tab); |
830 | return err; |
831 | } |
832 | |
833 | static int |
834 | build_btf_tables(struct hashmap *btf_prog_table, |
835 | struct hashmap *btf_map_table) |
836 | { |
837 | struct bpf_prog_info prog_info; |
838 | __u32 prog_len = sizeof(prog_info); |
839 | struct bpf_map_info map_info; |
840 | __u32 map_len = sizeof(map_info); |
841 | int err = 0; |
842 | |
843 | err = build_btf_type_table(tab: btf_prog_table, type: BPF_OBJ_PROG, info: &prog_info, |
844 | len: &prog_len); |
845 | if (err) |
846 | return err; |
847 | |
848 | err = build_btf_type_table(tab: btf_map_table, type: BPF_OBJ_MAP, info: &map_info, |
849 | len: &map_len); |
850 | if (err) { |
851 | hashmap__free(btf_prog_table); |
852 | return err; |
853 | } |
854 | |
855 | return 0; |
856 | } |
857 | |
858 | static void |
859 | show_btf_plain(struct bpf_btf_info *info, int fd, |
860 | struct hashmap *btf_prog_table, |
861 | struct hashmap *btf_map_table) |
862 | { |
863 | struct hashmap_entry *entry; |
864 | const char *name = u64_to_ptr(ptr: info->name); |
865 | int n; |
866 | |
867 | printf("%u: " , info->id); |
868 | if (info->kernel_btf) |
869 | printf("name [%s] " , name); |
870 | else if (name && name[0]) |
871 | printf("name %s " , name); |
872 | else |
873 | printf("name <anon> " ); |
874 | printf("size %uB" , info->btf_size); |
875 | |
876 | n = 0; |
877 | hashmap__for_each_key_entry(btf_prog_table, entry, info->id) { |
878 | printf("%s%lu" , n++ == 0 ? " prog_ids " : "," , entry->value); |
879 | } |
880 | |
881 | n = 0; |
882 | hashmap__for_each_key_entry(btf_map_table, entry, info->id) { |
883 | printf("%s%lu" , n++ == 0 ? " map_ids " : "," , entry->value); |
884 | } |
885 | |
886 | emit_obj_refs_plain(table: refs_table, id: info->id, prefix: "\n\tpids " ); |
887 | |
888 | printf("\n" ); |
889 | } |
890 | |
891 | static void |
892 | show_btf_json(struct bpf_btf_info *info, int fd, |
893 | struct hashmap *btf_prog_table, |
894 | struct hashmap *btf_map_table) |
895 | { |
896 | struct hashmap_entry *entry; |
897 | const char *name = u64_to_ptr(ptr: info->name); |
898 | |
899 | jsonw_start_object(self: json_wtr); /* btf object */ |
900 | jsonw_uint_field(self: json_wtr, prop: "id" , num: info->id); |
901 | jsonw_uint_field(self: json_wtr, prop: "size" , num: info->btf_size); |
902 | |
903 | jsonw_name(self: json_wtr, name: "prog_ids" ); |
904 | jsonw_start_array(self: json_wtr); /* prog_ids */ |
905 | hashmap__for_each_key_entry(btf_prog_table, entry, info->id) { |
906 | jsonw_uint(self: json_wtr, number: entry->value); |
907 | } |
908 | jsonw_end_array(self: json_wtr); /* prog_ids */ |
909 | |
910 | jsonw_name(self: json_wtr, name: "map_ids" ); |
911 | jsonw_start_array(self: json_wtr); /* map_ids */ |
912 | hashmap__for_each_key_entry(btf_map_table, entry, info->id) { |
913 | jsonw_uint(self: json_wtr, number: entry->value); |
914 | } |
915 | jsonw_end_array(self: json_wtr); /* map_ids */ |
916 | |
917 | emit_obj_refs_json(table: refs_table, id: info->id, json_wtr); /* pids */ |
918 | |
919 | jsonw_bool_field(self: json_wtr, prop: "kernel" , value: info->kernel_btf); |
920 | |
921 | if (name && name[0]) |
922 | jsonw_string_field(self: json_wtr, prop: "name" , val: name); |
923 | |
924 | jsonw_end_object(self: json_wtr); /* btf object */ |
925 | } |
926 | |
927 | static int |
928 | show_btf(int fd, struct hashmap *btf_prog_table, |
929 | struct hashmap *btf_map_table) |
930 | { |
931 | struct bpf_btf_info info; |
932 | __u32 len = sizeof(info); |
933 | char name[64]; |
934 | int err; |
935 | |
936 | memset(&info, 0, sizeof(info)); |
937 | err = bpf_btf_get_info_by_fd(fd, &info, &len); |
938 | if (err) { |
939 | p_err("can't get BTF object info: %s" , strerror(errno)); |
940 | return -1; |
941 | } |
942 | /* if kernel support emitting BTF object name, pass name pointer */ |
943 | if (info.name_len) { |
944 | memset(&info, 0, sizeof(info)); |
945 | info.name_len = sizeof(name); |
946 | info.name = ptr_to_u64(ptr: name); |
947 | len = sizeof(info); |
948 | |
949 | err = bpf_btf_get_info_by_fd(fd, &info, &len); |
950 | if (err) { |
951 | p_err("can't get BTF object info: %s" , strerror(errno)); |
952 | return -1; |
953 | } |
954 | } |
955 | |
956 | if (json_output) |
957 | show_btf_json(info: &info, fd, btf_prog_table, btf_map_table); |
958 | else |
959 | show_btf_plain(info: &info, fd, btf_prog_table, btf_map_table); |
960 | |
961 | return 0; |
962 | } |
963 | |
964 | static int do_show(int argc, char **argv) |
965 | { |
966 | struct hashmap *btf_prog_table; |
967 | struct hashmap *btf_map_table; |
968 | int err, fd = -1; |
969 | __u32 id = 0; |
970 | |
971 | if (argc == 2) { |
972 | fd = btf_parse_fd(argc: &argc, argv: &argv); |
973 | if (fd < 0) |
974 | return -1; |
975 | } |
976 | |
977 | if (argc) { |
978 | if (fd >= 0) |
979 | close(fd); |
980 | return BAD_ARG(); |
981 | } |
982 | |
983 | btf_prog_table = hashmap__new(hash_fn_for_key_as_id, |
984 | equal_fn_for_key_as_id, NULL); |
985 | btf_map_table = hashmap__new(hash_fn_for_key_as_id, |
986 | equal_fn_for_key_as_id, NULL); |
987 | if (IS_ERR(ptr: btf_prog_table) || IS_ERR(ptr: btf_map_table)) { |
988 | hashmap__free(btf_prog_table); |
989 | hashmap__free(btf_map_table); |
990 | if (fd >= 0) |
991 | close(fd); |
992 | p_err(fmt: "failed to create hashmap for object references" ); |
993 | return -1; |
994 | } |
995 | err = build_btf_tables(btf_prog_table, btf_map_table); |
996 | if (err) { |
997 | if (fd >= 0) |
998 | close(fd); |
999 | return err; |
1000 | } |
1001 | build_obj_refs_table(table: &refs_table, type: BPF_OBJ_BTF); |
1002 | |
1003 | if (fd >= 0) { |
1004 | err = show_btf(fd, btf_prog_table, btf_map_table); |
1005 | close(fd); |
1006 | goto exit_free; |
1007 | } |
1008 | |
1009 | if (json_output) |
1010 | jsonw_start_array(self: json_wtr); /* root array */ |
1011 | |
1012 | while (true) { |
1013 | err = bpf_btf_get_next_id(id, &id); |
1014 | if (err) { |
1015 | if (errno == ENOENT) { |
1016 | err = 0; |
1017 | break; |
1018 | } |
1019 | p_err("can't get next BTF object: %s%s" , |
1020 | strerror(errno), |
1021 | errno == EINVAL ? " -- kernel too old?" : "" ); |
1022 | err = -1; |
1023 | break; |
1024 | } |
1025 | |
1026 | fd = bpf_btf_get_fd_by_id(id); |
1027 | if (fd < 0) { |
1028 | if (errno == ENOENT) |
1029 | continue; |
1030 | p_err("can't get BTF object by id (%u): %s" , |
1031 | id, strerror(errno)); |
1032 | err = -1; |
1033 | break; |
1034 | } |
1035 | |
1036 | err = show_btf(fd, btf_prog_table, btf_map_table); |
1037 | close(fd); |
1038 | if (err) |
1039 | break; |
1040 | } |
1041 | |
1042 | if (json_output) |
1043 | jsonw_end_array(self: json_wtr); /* root array */ |
1044 | |
1045 | exit_free: |
1046 | hashmap__free(btf_prog_table); |
1047 | hashmap__free(btf_map_table); |
1048 | delete_obj_refs_table(table: refs_table); |
1049 | |
1050 | return err; |
1051 | } |
1052 | |
1053 | static int do_help(int argc, char **argv) |
1054 | { |
1055 | if (json_output) { |
1056 | jsonw_null(self: json_wtr); |
1057 | return 0; |
1058 | } |
1059 | |
1060 | fprintf(stderr, |
1061 | "Usage: %1$s %2$s { show | list } [id BTF_ID]\n" |
1062 | " %1$s %2$s dump BTF_SRC [format FORMAT]\n" |
1063 | " %1$s %2$s help\n" |
1064 | "\n" |
1065 | " BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n" |
1066 | " FORMAT := { raw | c }\n" |
1067 | " " HELP_SPEC_MAP "\n" |
1068 | " " HELP_SPEC_PROGRAM "\n" |
1069 | " " HELP_SPEC_OPTIONS " |\n" |
1070 | " {-B|--base-btf} }\n" |
1071 | "" , |
1072 | bin_name, "btf" ); |
1073 | |
1074 | return 0; |
1075 | } |
1076 | |
1077 | static const struct cmd cmds[] = { |
1078 | { "show" , do_show }, |
1079 | { "list" , do_show }, |
1080 | { "help" , do_help }, |
1081 | { "dump" , do_dump }, |
1082 | { 0 } |
1083 | }; |
1084 | |
1085 | int do_btf(int argc, char **argv) |
1086 | { |
1087 | return cmd_select(cmds, argc, argv, help: do_help); |
1088 | } |
1089 | |