aboutsummaryrefslogtreecommitdiffstats
path: root/tools/lib/bpf/btf.c
diff options
context:
space:
mode:
authorYonghong Song <yhs@fb.com>2018-11-19 18:29:16 -0500
committerAlexei Starovoitov <ast@kernel.org>2018-11-20 13:54:39 -0500
commit2993e0515bb44e157c17c9ba7309ba46366b6add (patch)
tree15c44288f87b0cb93cbfc6012e0094fc106ecdd7 /tools/lib/bpf/btf.c
parent4798c4ba3ba94e4da37b2557dfda04f80a94e8d5 (diff)
tools/bpf: add support to read .BTF.ext sections
The .BTF section is already available to encode types. These types can be used for map pretty print. The whole .BTF will be passed to the kernel as well for which kernel can verify and return to the user space for pretty print etc. The llvm patch at https://reviews.llvm.org/D53736 will generate .BTF section and one more section .BTF.ext. The .BTF.ext section encodes function type information and line information. Note that this patch set only supports function type info. The functionality is implemented in libbpf. The .BTF section can be directly loaded into the kernel, and the .BTF.ext section cannot. The loader may need to do some relocation and merging, similar to merging multiple code sections, before loading into the kernel. Signed-off-by: Yonghong Song <yhs@fb.com> Signed-off-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'tools/lib/bpf/btf.c')
-rw-r--r--tools/lib/bpf/btf.c274
1 files changed, 274 insertions, 0 deletions
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 31225e64766f..fe87cb48a6a9 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -37,6 +37,18 @@ struct btf {
37 int fd; 37 int fd;
38}; 38};
39 39
40struct btf_ext {
41 void *func_info;
42 __u32 func_info_rec_size;
43 __u32 func_info_len;
44};
45
46/* The minimum bpf_func_info checked by the loader */
47struct bpf_func_info_min {
48 __u32 insn_offset;
49 __u32 type_id;
50};
51
40static int btf_add_type(struct btf *btf, struct btf_type *t) 52static int btf_add_type(struct btf *btf, struct btf_type *t)
41{ 53{
42 if (btf->types_size - btf->nr_types < 2) { 54 if (btf->types_size - btf->nr_types < 2) {
@@ -397,3 +409,265 @@ const char *btf__name_by_offset(const struct btf *btf, __u32 offset)
397 else 409 else
398 return NULL; 410 return NULL;
399} 411}
412
413static int btf_ext_validate_func_info(const void *finfo, __u32 size,
414 btf_print_fn_t err_log)
415{
416 int sec_hdrlen = sizeof(struct btf_sec_func_info);
417 __u32 size_left, num_records, record_size;
418 const struct btf_sec_func_info *sinfo;
419 __u64 total_record_size;
420
421 /* At least a func_info record size */
422 if (size < sizeof(__u32)) {
423 elog("BTF.ext func_info record size not found");
424 return -EINVAL;
425 }
426
427 /* The record size needs to meet below minimum standard */
428 record_size = *(__u32 *)finfo;
429 if (record_size < sizeof(struct bpf_func_info_min) ||
430 record_size % sizeof(__u32)) {
431 elog("BTF.ext func_info invalid record size");
432 return -EINVAL;
433 }
434
435 sinfo = finfo + sizeof(__u32);
436 size_left = size - sizeof(__u32);
437
438 /* If no func_info records, return failure now so .BTF.ext
439 * won't be used.
440 */
441 if (!size_left) {
442 elog("BTF.ext no func info records");
443 return -EINVAL;
444 }
445
446 while (size_left) {
447 if (size_left < sec_hdrlen) {
448 elog("BTF.ext func_info header not found");
449 return -EINVAL;
450 }
451
452 num_records = sinfo->num_func_info;
453 if (num_records == 0) {
454 elog("incorrect BTF.ext num_func_info");
455 return -EINVAL;
456 }
457
458 total_record_size = sec_hdrlen +
459 (__u64)num_records * record_size;
460 if (size_left < total_record_size) {
461 elog("incorrect BTF.ext num_func_info");
462 return -EINVAL;
463 }
464
465 size_left -= total_record_size;
466 sinfo = (void *)sinfo + total_record_size;
467 }
468
469 return 0;
470}
471
472static int btf_ext_parse_hdr(__u8 *data, __u32 data_size,
473 btf_print_fn_t err_log)
474{
475 const struct btf_ext_header *hdr = (struct btf_ext_header *)data;
476 __u32 meta_left, last_func_info_pos;
477 void *finfo;
478
479 if (data_size < offsetof(struct btf_ext_header, func_info_off) ||
480 data_size < hdr->hdr_len) {
481 elog("BTF.ext header not found");
482 return -EINVAL;
483 }
484
485 if (hdr->magic != BTF_MAGIC) {
486 elog("Invalid BTF.ext magic:%x\n", hdr->magic);
487 return -EINVAL;
488 }
489
490 if (hdr->version != BTF_VERSION) {
491 elog("Unsupported BTF.ext version:%u\n", hdr->version);
492 return -ENOTSUP;
493 }
494
495 if (hdr->flags) {
496 elog("Unsupported BTF.ext flags:%x\n", hdr->flags);
497 return -ENOTSUP;
498 }
499
500 meta_left = data_size - hdr->hdr_len;
501 if (!meta_left) {
502 elog("BTF.ext has no data\n");
503 return -EINVAL;
504 }
505
506 if (meta_left < hdr->func_info_off) {
507 elog("Invalid BTF.ext func_info section offset:%u\n",
508 hdr->func_info_off);
509 return -EINVAL;
510 }
511
512 if (hdr->func_info_off & 0x03) {
513 elog("BTF.ext func_info section is not aligned to 4 bytes\n");
514 return -EINVAL;
515 }
516
517 last_func_info_pos = hdr->hdr_len + hdr->func_info_off +
518 hdr->func_info_len;
519 if (last_func_info_pos > data_size) {
520 elog("Invalid BTF.ext func_info section size:%u\n",
521 hdr->func_info_len);
522 return -EINVAL;
523 }
524
525 finfo = data + hdr->hdr_len + hdr->func_info_off;
526 return btf_ext_validate_func_info(finfo, hdr->func_info_len,
527 err_log);
528}
529
530void btf_ext__free(struct btf_ext *btf_ext)
531{
532 if (!btf_ext)
533 return;
534
535 free(btf_ext->func_info);
536 free(btf_ext);
537}
538
539struct btf_ext *btf_ext__new(__u8 *data, __u32 size, btf_print_fn_t err_log)
540{
541 const struct btf_ext_header *hdr;
542 struct btf_ext *btf_ext;
543 void *org_fdata, *fdata;
544 __u32 hdrlen, size_u32;
545 int err;
546
547 err = btf_ext_parse_hdr(data, size, err_log);
548 if (err)
549 return ERR_PTR(err);
550
551 btf_ext = calloc(1, sizeof(struct btf_ext));
552 if (!btf_ext)
553 return ERR_PTR(-ENOMEM);
554
555 hdr = (const struct btf_ext_header *)data;
556 hdrlen = hdr->hdr_len;
557 size_u32 = sizeof(__u32);
558 fdata = malloc(hdr->func_info_len - size_u32);
559 if (!fdata) {
560 free(btf_ext);
561 return ERR_PTR(-ENOMEM);
562 }
563
564 /* remember record size and copy rest of func_info data */
565 org_fdata = data + hdrlen + hdr->func_info_off;
566 btf_ext->func_info_rec_size = *(__u32 *)org_fdata;
567 memcpy(fdata, org_fdata + size_u32, hdr->func_info_len - size_u32);
568 btf_ext->func_info = fdata;
569 btf_ext->func_info_len = hdr->func_info_len - size_u32;
570
571 return btf_ext;
572}
573
574int btf_ext__reloc_init(struct btf *btf, struct btf_ext *btf_ext,
575 const char *sec_name, void **func_info,
576 __u32 *func_info_rec_size, __u32 *func_info_len)
577{
578 __u32 sec_hdrlen = sizeof(struct btf_sec_func_info);
579 __u32 i, record_size, records_len;
580 struct btf_sec_func_info *sinfo;
581 const char *info_sec_name;
582 __s64 remain_len;
583 void *data;
584
585 record_size = btf_ext->func_info_rec_size;
586 sinfo = btf_ext->func_info;
587 remain_len = btf_ext->func_info_len;
588
589 while (remain_len > 0) {
590 records_len = sinfo->num_func_info * record_size;
591 info_sec_name = btf__name_by_offset(btf, sinfo->sec_name_off);
592 if (strcmp(info_sec_name, sec_name)) {
593 remain_len -= sec_hdrlen + records_len;
594 sinfo = (void *)sinfo + sec_hdrlen + records_len;
595 continue;
596 }
597
598 data = malloc(records_len);
599 if (!data)
600 return -ENOMEM;
601
602 memcpy(data, sinfo->data, records_len);
603
604 /* adjust the insn_offset, the data in .BTF.ext is
605 * the actual byte offset, and the kernel expects
606 * the offset in term of bpf_insn.
607 *
608 * adjust the insn offset only, the rest data will
609 * be passed to kernel.
610 */
611 for (i = 0; i < sinfo->num_func_info; i++) {
612 struct bpf_func_info_min *record;
613
614 record = data + i * record_size;
615 record->insn_offset /= sizeof(struct bpf_insn);
616 }
617
618 *func_info = data;
619 *func_info_len = records_len;
620 *func_info_rec_size = record_size;
621 return 0;
622 }
623
624 return -EINVAL;
625}
626
627int btf_ext__reloc(struct btf *btf, struct btf_ext *btf_ext,
628 const char *sec_name, __u32 insns_cnt,
629 void **func_info, __u32 *func_info_len)
630{
631 __u32 sec_hdrlen = sizeof(struct btf_sec_func_info);
632 __u32 i, record_size, existing_flen, records_len;
633 struct btf_sec_func_info *sinfo;
634 const char *info_sec_name;
635 __u64 remain_len;
636 void *data;
637
638 record_size = btf_ext->func_info_rec_size;
639 sinfo = btf_ext->func_info;
640 remain_len = btf_ext->func_info_len;
641 while (remain_len > 0) {
642 records_len = sinfo->num_func_info * record_size;
643 info_sec_name = btf__name_by_offset(btf, sinfo->sec_name_off);
644 if (strcmp(info_sec_name, sec_name)) {
645 remain_len -= sec_hdrlen + records_len;
646 sinfo = (void *)sinfo + sec_hdrlen + records_len;
647 continue;
648 }
649
650 existing_flen = *func_info_len;
651 data = realloc(*func_info, existing_flen + records_len);
652 if (!data)
653 return -ENOMEM;
654
655 memcpy(data + existing_flen, sinfo->data, records_len);
656 /* adjust insn_offset only, the rest data will be passed
657 * to the kernel.
658 */
659 for (i = 0; i < sinfo->num_func_info; i++) {
660 struct bpf_func_info_min *record;
661
662 record = data + existing_flen + i * record_size;
663 record->insn_offset =
664 record->insn_offset / sizeof(struct bpf_insn) +
665 insns_cnt;
666 }
667 *func_info = data;
668 *func_info_len = existing_flen + records_len;
669 return 0;
670 }
671
672 return -EINVAL;
673}