@@ -37,6 +37,18 @@ struct btf {
37
37
int fd ;
38
38
};
39
39
40
+ struct 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 */
47
+ struct bpf_func_info_min {
48
+ __u32 insn_offset ;
49
+ __u32 type_id ;
50
+ };
51
+
40
52
static int btf_add_type (struct btf * btf , struct btf_type * t )
41
53
{
42
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
409
else
398
410
return NULL ;
399
411
}
412
+
413
+ static 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
+
472
+ static 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
+
530
+ void 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
+
539
+ struct 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
+
574
+ int 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
+
627
+ int 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
+ }
0 commit comments