Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: libbpf: Add btf_line_info support to libbpf

This patch adds bpf_line_info support to libbpf:
1) Parsing the line_info sec from ".BTF.ext"
2) Relocating the line_info. If the main prog *_info relocation
fails, it will ignore the remaining subprog line_info and continue.
If the subprog *_info relocation fails, it will bail out.
3) BPF_PROG_LOAD a prog with line_info

Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Acked-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Martin KaFai Lau and committed by
Alexei Starovoitov
3d650141 f0187f0b

+238 -88
+55 -27
tools/lib/bpf/bpf.c
··· 173 173 -1); 174 174 } 175 175 176 + static void * 177 + alloc_zero_tailing_info(const void *orecord, __u32 cnt, 178 + __u32 actual_rec_size, __u32 expected_rec_size) 179 + { 180 + __u64 info_len = actual_rec_size * cnt; 181 + void *info, *nrecord; 182 + int i; 183 + 184 + info = malloc(info_len); 185 + if (!info) 186 + return NULL; 187 + 188 + /* zero out bytes kernel does not understand */ 189 + nrecord = info; 190 + for (i = 0; i < cnt; i++) { 191 + memcpy(nrecord, orecord, expected_rec_size); 192 + memset(nrecord + expected_rec_size, 0, 193 + actual_rec_size - expected_rec_size); 194 + orecord += actual_rec_size; 195 + nrecord += actual_rec_size; 196 + } 197 + 198 + return info; 199 + } 200 + 176 201 int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr, 177 202 char *log_buf, size_t log_buf_sz) 178 203 { 204 + void *finfo = NULL, *linfo = NULL; 179 205 union bpf_attr attr; 180 - void *finfo = NULL; 181 206 __u32 name_len; 182 207 int fd; 183 208 ··· 226 201 attr.func_info_rec_size = load_attr->func_info_rec_size; 227 202 attr.func_info_cnt = load_attr->func_info_cnt; 228 203 attr.func_info = ptr_to_u64(load_attr->func_info); 204 + attr.line_info_rec_size = load_attr->line_info_rec_size; 205 + attr.line_info_cnt = load_attr->line_info_cnt; 206 + attr.line_info = ptr_to_u64(load_attr->line_info); 229 207 memcpy(attr.prog_name, load_attr->name, 230 208 min(name_len, BPF_OBJ_NAME_LEN - 1)); 231 209 ··· 240 212 * to give user space a hint how to deal with loading failure. 241 213 * Check to see whether we can make some changes and load again. 242 214 */ 243 - if (errno == E2BIG && attr.func_info_cnt && 244 - attr.func_info_rec_size < load_attr->func_info_rec_size) { 245 - __u32 actual_rec_size = load_attr->func_info_rec_size; 246 - __u32 expected_rec_size = attr.func_info_rec_size; 247 - __u32 finfo_cnt = load_attr->func_info_cnt; 248 - __u64 finfo_len = actual_rec_size * finfo_cnt; 249 - const void *orecord; 250 - void *nrecord; 251 - int i; 215 + while (errno == E2BIG && (!finfo || !linfo)) { 216 + if (!finfo && attr.func_info_cnt && 217 + attr.func_info_rec_size < load_attr->func_info_rec_size) { 218 + /* try with corrected func info records */ 219 + finfo = alloc_zero_tailing_info(load_attr->func_info, 220 + load_attr->func_info_cnt, 221 + load_attr->func_info_rec_size, 222 + attr.func_info_rec_size); 223 + if (!finfo) 224 + goto done; 252 225 253 - finfo = malloc(finfo_len); 254 - if (!finfo) 255 - /* further try with log buffer won't help */ 256 - return fd; 226 + attr.func_info = ptr_to_u64(finfo); 227 + attr.func_info_rec_size = load_attr->func_info_rec_size; 228 + } else if (!linfo && attr.line_info_cnt && 229 + attr.line_info_rec_size < 230 + load_attr->line_info_rec_size) { 231 + linfo = alloc_zero_tailing_info(load_attr->line_info, 232 + load_attr->line_info_cnt, 233 + load_attr->line_info_rec_size, 234 + attr.line_info_rec_size); 235 + if (!linfo) 236 + goto done; 257 237 258 - /* zero out bytes kernel does not understand */ 259 - orecord = load_attr->func_info; 260 - nrecord = finfo; 261 - for (i = 0; i < load_attr->func_info_cnt; i++) { 262 - memcpy(nrecord, orecord, expected_rec_size); 263 - memset(nrecord + expected_rec_size, 0, 264 - actual_rec_size - expected_rec_size); 265 - orecord += actual_rec_size; 266 - nrecord += actual_rec_size; 238 + attr.line_info = ptr_to_u64(linfo); 239 + attr.line_info_rec_size = load_attr->line_info_rec_size; 240 + } else { 241 + break; 267 242 } 268 - 269 - /* try with corrected func info records */ 270 - attr.func_info = ptr_to_u64(finfo); 271 - attr.func_info_rec_size = load_attr->func_info_rec_size; 272 243 273 244 fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); 274 245 ··· 286 259 fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); 287 260 done: 288 261 free(finfo); 262 + free(linfo); 289 263 return fd; 290 264 } 291 265
+3
tools/lib/bpf/bpf.h
··· 82 82 __u32 func_info_rec_size; 83 83 const void *func_info; 84 84 __u32 func_info_cnt; 85 + __u32 line_info_rec_size; 86 + const void *line_info; 87 + __u32 line_info_cnt; 85 88 }; 86 89 87 90 /* Flags to direct loading requirements */
+151 -60
tools/lib/bpf/btf.c
··· 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; 40 + struct btf_ext_info { 41 + /* 42 + * info points to a deep copy of the individual info section 43 + * (e.g. func_info and line_info) from the .BTF.ext. 44 + * It does not include the __u32 rec_size. 45 + */ 46 + void *info; 47 + __u32 rec_size; 48 + __u32 len; 44 49 }; 45 50 46 - struct btf_sec_func_info { 51 + struct btf_ext { 52 + struct btf_ext_info func_info; 53 + struct btf_ext_info line_info; 54 + }; 55 + 56 + struct btf_ext_info_sec { 47 57 __u32 sec_name_off; 48 - __u32 num_func_info; 49 - /* Followed by num_func_info number of bpf func_info records */ 58 + __u32 num_info; 59 + /* Followed by num_info * record_size number of bytes */ 50 60 __u8 data[0]; 51 61 }; 52 62 ··· 64 54 struct bpf_func_info_min { 65 55 __u32 insn_off; 66 56 __u32 type_id; 57 + }; 58 + 59 + /* The minimum bpf_line_info checked by the loader */ 60 + struct bpf_line_info_min { 61 + __u32 insn_off; 62 + __u32 file_name_off; 63 + __u32 line_off; 64 + __u32 line_col; 67 65 }; 68 66 69 67 static inline __u64 ptr_to_u64(const void *ptr) ··· 504 486 return err; 505 487 } 506 488 507 - static int btf_ext_copy_func_info(struct btf_ext *btf_ext, 508 - __u8 *data, __u32 data_size, 509 - btf_print_fn_t err_log) 489 + struct btf_ext_sec_copy_param { 490 + __u32 off; 491 + __u32 len; 492 + __u32 min_rec_size; 493 + struct btf_ext_info *ext_info; 494 + const char *desc; 495 + }; 496 + 497 + static int btf_ext_copy_info(struct btf_ext *btf_ext, 498 + __u8 *data, __u32 data_size, 499 + struct btf_ext_sec_copy_param *ext_sec, 500 + btf_print_fn_t err_log) 510 501 { 511 502 const struct btf_ext_header *hdr = (struct btf_ext_header *)data; 512 - const struct btf_sec_func_info *sinfo; 503 + const struct btf_ext_info_sec *sinfo; 504 + struct btf_ext_info *ext_info; 513 505 __u32 info_left, record_size; 514 506 /* The start of the info sec (including the __u32 record_size). */ 515 507 const void *info; ··· 528 500 data = data + hdr->hdr_len; 529 501 data_size -= hdr->hdr_len; 530 502 531 - if (hdr->func_info_off & 0x03) { 532 - elog("BTF.ext func_info section is not aligned to 4 bytes\n"); 503 + if (ext_sec->off & 0x03) { 504 + elog(".BTF.ext %s section is not aligned to 4 bytes\n", 505 + ext_sec->desc); 533 506 return -EINVAL; 534 507 } 535 508 536 - if (data_size < hdr->func_info_off || 537 - hdr->func_info_len > data_size - hdr->func_info_off) { 538 - elog("func_info section (off:%u len:%u) is beyond the end of the ELF section .BTF.ext\n", 539 - hdr->func_info_off, hdr->func_info_len); 509 + if (data_size < ext_sec->off || 510 + ext_sec->len > data_size - ext_sec->off) { 511 + elog("%s section (off:%u len:%u) is beyond the end of the ELF section .BTF.ext\n", 512 + ext_sec->desc, ext_sec->off, ext_sec->len); 540 513 return -EINVAL; 541 514 } 542 515 543 - info = data + hdr->func_info_off; 544 - info_left = hdr->func_info_len; 516 + info = data + ext_sec->off; 517 + info_left = ext_sec->len; 545 518 546 - /* At least a func_info record size */ 519 + /* At least a record size */ 547 520 if (info_left < sizeof(__u32)) { 548 - elog("BTF.ext func_info record size not found"); 521 + elog(".BTF.ext %s record size not found\n", ext_sec->desc); 549 522 return -EINVAL; 550 523 } 551 524 552 525 /* The record size needs to meet the minimum standard */ 553 526 record_size = *(__u32 *)info; 554 - if (record_size < sizeof(struct bpf_func_info_min) || 527 + if (record_size < ext_sec->min_rec_size || 555 528 record_size & 0x03) { 556 - elog("BTF.ext func_info invalid record size"); 529 + elog("%s section in .BTF.ext has invalid record size %u\n", 530 + ext_sec->desc, record_size); 557 531 return -EINVAL; 558 532 } 559 533 560 534 sinfo = info + sizeof(__u32); 561 535 info_left -= sizeof(__u32); 562 536 563 - /* If no func_info records, return failure now so .BTF.ext 564 - * won't be used. 565 - */ 537 + /* If no records, return failure now so .BTF.ext won't be used. */ 566 538 if (!info_left) { 567 - elog("BTF.ext no func info records"); 539 + elog("%s section in .BTF.ext has no records", ext_sec->desc); 568 540 return -EINVAL; 569 541 } 570 542 571 543 while (info_left) { 572 - unsigned int sec_hdrlen = sizeof(struct btf_sec_func_info); 544 + unsigned int sec_hdrlen = sizeof(struct btf_ext_info_sec); 573 545 __u64 total_record_size; 574 546 __u32 num_records; 575 547 576 548 if (info_left < sec_hdrlen) { 577 - elog("BTF.ext func_info header not found"); 549 + elog("%s section header is not found in .BTF.ext\n", 550 + ext_sec->desc); 578 551 return -EINVAL; 579 552 } 580 553 581 - num_records = sinfo->num_func_info; 554 + num_records = sinfo->num_info; 582 555 if (num_records == 0) { 583 - elog("incorrect BTF.ext num_func_info"); 556 + elog("%s section has incorrect num_records in .BTF.ext\n", 557 + ext_sec->desc); 584 558 return -EINVAL; 585 559 } 586 560 587 561 total_record_size = sec_hdrlen + 588 562 (__u64)num_records * record_size; 589 563 if (info_left < total_record_size) { 590 - elog("incorrect BTF.ext num_func_info"); 564 + elog("%s section has incorrect num_records in .BTF.ext\n", 565 + ext_sec->desc); 591 566 return -EINVAL; 592 567 } 593 568 ··· 598 567 sinfo = (void *)sinfo + total_record_size; 599 568 } 600 569 601 - btf_ext->func_info_len = hdr->func_info_len - sizeof(__u32); 602 - btf_ext->func_info_rec_size = record_size; 603 - btf_ext->func_info = malloc(btf_ext->func_info_len); 604 - if (!btf_ext->func_info) 570 + ext_info = ext_sec->ext_info; 571 + ext_info->len = ext_sec->len - sizeof(__u32); 572 + ext_info->rec_size = record_size; 573 + ext_info->info = malloc(ext_info->len); 574 + if (!ext_info->info) 605 575 return -ENOMEM; 606 - memcpy(btf_ext->func_info, info + sizeof(__u32), 607 - btf_ext->func_info_len); 576 + memcpy(ext_info->info, info + sizeof(__u32), ext_info->len); 608 577 609 578 return 0; 579 + } 580 + 581 + static int btf_ext_copy_func_info(struct btf_ext *btf_ext, 582 + __u8 *data, __u32 data_size, 583 + btf_print_fn_t err_log) 584 + { 585 + const struct btf_ext_header *hdr = (struct btf_ext_header *)data; 586 + struct btf_ext_sec_copy_param param = { 587 + .off = hdr->func_info_off, 588 + .len = hdr->func_info_len, 589 + .min_rec_size = sizeof(struct bpf_func_info_min), 590 + .ext_info = &btf_ext->func_info, 591 + .desc = "func_info" 592 + }; 593 + 594 + return btf_ext_copy_info(btf_ext, data, data_size, &param, err_log); 595 + } 596 + 597 + static int btf_ext_copy_line_info(struct btf_ext *btf_ext, 598 + __u8 *data, __u32 data_size, 599 + btf_print_fn_t err_log) 600 + { 601 + const struct btf_ext_header *hdr = (struct btf_ext_header *)data; 602 + struct btf_ext_sec_copy_param param = { 603 + .off = hdr->line_info_off, 604 + .len = hdr->line_info_len, 605 + .min_rec_size = sizeof(struct bpf_line_info_min), 606 + .ext_info = &btf_ext->line_info, 607 + .desc = "line_info", 608 + }; 609 + 610 + return btf_ext_copy_info(btf_ext, data, data_size, &param, err_log); 610 611 } 611 612 612 613 static int btf_ext_parse_hdr(__u8 *data, __u32 data_size, ··· 680 617 if (!btf_ext) 681 618 return; 682 619 683 - free(btf_ext->func_info); 620 + free(btf_ext->func_info.info); 621 + free(btf_ext->line_info.info); 684 622 free(btf_ext); 685 623 } 686 624 ··· 704 640 return ERR_PTR(err); 705 641 } 706 642 643 + err = btf_ext_copy_line_info(btf_ext, data, size, err_log); 644 + if (err) { 645 + btf_ext__free(btf_ext); 646 + return ERR_PTR(err); 647 + } 648 + 707 649 return btf_ext; 708 650 } 709 651 710 - int btf_ext__reloc_func_info(struct btf *btf, struct btf_ext *btf_ext, 711 - const char *sec_name, __u32 insns_cnt, 712 - void **func_info, __u32 *cnt) 652 + static int btf_ext_reloc_info(const struct btf *btf, 653 + const struct btf_ext_info *ext_info, 654 + const char *sec_name, __u32 insns_cnt, 655 + void **info, __u32 *cnt) 713 656 { 714 - __u32 sec_hdrlen = sizeof(struct btf_sec_func_info); 715 - __u32 i, record_size, existing_flen, records_len; 716 - struct btf_sec_func_info *sinfo; 657 + __u32 sec_hdrlen = sizeof(struct btf_ext_info_sec); 658 + __u32 i, record_size, existing_len, records_len; 659 + struct btf_ext_info_sec *sinfo; 717 660 const char *info_sec_name; 718 661 __u64 remain_len; 719 662 void *data; 720 663 721 - record_size = btf_ext->func_info_rec_size; 722 - sinfo = btf_ext->func_info; 723 - remain_len = btf_ext->func_info_len; 664 + record_size = ext_info->rec_size; 665 + sinfo = ext_info->info; 666 + remain_len = ext_info->len; 724 667 while (remain_len > 0) { 725 - records_len = sinfo->num_func_info * record_size; 668 + records_len = sinfo->num_info * record_size; 726 669 info_sec_name = btf__name_by_offset(btf, sinfo->sec_name_off); 727 670 if (strcmp(info_sec_name, sec_name)) { 728 671 remain_len -= sec_hdrlen + records_len; ··· 737 666 continue; 738 667 } 739 668 740 - existing_flen = (*cnt) * record_size; 741 - data = realloc(*func_info, existing_flen + records_len); 669 + existing_len = (*cnt) * record_size; 670 + data = realloc(*info, existing_len + records_len); 742 671 if (!data) 743 672 return -ENOMEM; 744 673 745 - memcpy(data + existing_flen, sinfo->data, records_len); 674 + memcpy(data + existing_len, sinfo->data, records_len); 746 675 /* adjust insn_off only, the rest data will be passed 747 676 * to the kernel. 748 677 */ 749 - for (i = 0; i < sinfo->num_func_info; i++) { 750 - struct bpf_func_info_min *record; 678 + for (i = 0; i < sinfo->num_info; i++) { 679 + __u32 *insn_off; 751 680 752 - record = data + existing_flen + i * record_size; 753 - record->insn_off = 754 - record->insn_off / sizeof(struct bpf_insn) + 681 + insn_off = data + existing_len + (i * record_size); 682 + *insn_off = *insn_off / sizeof(struct bpf_insn) + 755 683 insns_cnt; 756 684 } 757 - *func_info = data; 758 - *cnt += sinfo->num_func_info; 685 + *info = data; 686 + *cnt += sinfo->num_info; 759 687 return 0; 760 688 } 761 689 762 690 return -ENOENT; 763 691 } 764 692 693 + int btf_ext__reloc_func_info(const struct btf *btf, const struct btf_ext *btf_ext, 694 + const char *sec_name, __u32 insns_cnt, 695 + void **func_info, __u32 *cnt) 696 + { 697 + return btf_ext_reloc_info(btf, &btf_ext->func_info, sec_name, 698 + insns_cnt, func_info, cnt); 699 + } 700 + 701 + int btf_ext__reloc_line_info(const struct btf *btf, const struct btf_ext *btf_ext, 702 + const char *sec_name, __u32 insns_cnt, 703 + void **line_info, __u32 *cnt) 704 + { 705 + return btf_ext_reloc_info(btf, &btf_ext->line_info, sec_name, 706 + insns_cnt, line_info, cnt); 707 + } 708 + 765 709 __u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext) 766 710 { 767 - return btf_ext->func_info_rec_size; 711 + return btf_ext->func_info.rec_size; 712 + } 713 + 714 + __u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext) 715 + { 716 + return btf_ext->line_info.rec_size; 768 717 }
+9 -1
tools/lib/bpf/btf.h
··· 51 51 /* All offsets are in bytes relative to the end of this header */ 52 52 __u32 func_info_off; 53 53 __u32 func_info_len; 54 + __u32 line_info_off; 55 + __u32 line_info_len; 54 56 }; 55 57 56 58 typedef int (*btf_print_fn_t)(const char *, ...) ··· 72 70 73 71 struct btf_ext *btf_ext__new(__u8 *data, __u32 size, btf_print_fn_t err_log); 74 72 void btf_ext__free(struct btf_ext *btf_ext); 75 - int btf_ext__reloc_func_info(struct btf *btf, struct btf_ext *btf_ext, 73 + int btf_ext__reloc_func_info(const struct btf *btf, 74 + const struct btf_ext *btf_ext, 76 75 const char *sec_name, __u32 insns_cnt, 77 76 void **func_info, __u32 *func_info_len); 77 + int btf_ext__reloc_line_info(const struct btf *btf, 78 + const struct btf_ext *btf_ext, 79 + const char *sec_name, __u32 insns_cnt, 80 + void **line_info, __u32 *cnt); 78 81 __u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext); 82 + __u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext); 79 83 80 84 #ifdef __cplusplus 81 85 } /* extern "C" */
+20
tools/lib/bpf/libbpf.c
··· 170 170 __u32 func_info_cnt; 171 171 172 172 struct bpf_capabilities *caps; 173 + 174 + void *line_info; 175 + __u32 line_info_rec_size; 176 + __u32 line_info_cnt; 173 177 }; 174 178 175 179 struct bpf_map { ··· 1346 1342 prog->func_info_rec_size = btf_ext__func_info_rec_size(obj->btf_ext); 1347 1343 } 1348 1344 1345 + if (!insn_offset || prog->line_info) { 1346 + err = btf_ext__reloc_line_info(obj->btf, obj->btf_ext, 1347 + section_name, insn_offset, 1348 + &prog->line_info, 1349 + &prog->line_info_cnt); 1350 + if (err) 1351 + return check_btf_ext_reloc_err(prog, err, 1352 + prog->line_info, 1353 + "bpf_line_info"); 1354 + 1355 + prog->line_info_rec_size = btf_ext__line_info_rec_size(obj->btf_ext); 1356 + } 1357 + 1349 1358 if (!insn_offset) 1350 1359 prog->btf_fd = btf__fd(obj->btf); 1351 1360 ··· 1543 1526 load_attr.func_info = prog->func_info; 1544 1527 load_attr.func_info_rec_size = prog->func_info_rec_size; 1545 1528 load_attr.func_info_cnt = prog->func_info_cnt; 1529 + load_attr.line_info = prog->line_info; 1530 + load_attr.line_info_rec_size = prog->line_info_rec_size; 1531 + load_attr.line_info_cnt = prog->line_info_cnt; 1546 1532 if (!load_attr.insns || !load_attr.insns_cnt) 1547 1533 return -EINVAL; 1548 1534