Skip to content

Commit 2993e05

Browse files
yonghong-songAlexei Starovoitov
authored andcommitted
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>
1 parent 4798c4b commit 2993e05

File tree

4 files changed

+442
-15
lines changed

4 files changed

+442
-15
lines changed

tools/lib/bpf/bpf.c

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
186186
char *log_buf, size_t log_buf_sz)
187187
{
188188
union bpf_attr attr;
189+
void *finfo = NULL;
189190
__u32 name_len;
190191
int fd;
191192

@@ -216,12 +217,55 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
216217
if (fd >= 0 || !log_buf || !log_buf_sz)
217218
return fd;
218219

220+
/* After bpf_prog_load, the kernel may modify certain attributes
221+
* to give user space a hint how to deal with loading failure.
222+
* Check to see whether we can make some changes and load again.
223+
*/
224+
if (errno == E2BIG && attr.func_info_cnt &&
225+
attr.func_info_rec_size < load_attr->func_info_rec_size) {
226+
__u32 actual_rec_size = load_attr->func_info_rec_size;
227+
__u32 expected_rec_size = attr.func_info_rec_size;
228+
__u32 finfo_cnt = load_attr->func_info_cnt;
229+
__u64 finfo_len = actual_rec_size * finfo_cnt;
230+
const void *orecord;
231+
void *nrecord;
232+
int i;
233+
234+
finfo = malloc(finfo_len);
235+
if (!finfo)
236+
/* further try with log buffer won't help */
237+
return fd;
238+
239+
/* zero out bytes kernel does not understand */
240+
orecord = load_attr->func_info;
241+
nrecord = finfo;
242+
for (i = 0; i < load_attr->func_info_cnt; i++) {
243+
memcpy(nrecord, orecord, expected_rec_size);
244+
memset(nrecord + expected_rec_size, 0,
245+
actual_rec_size - expected_rec_size);
246+
orecord += actual_rec_size;
247+
nrecord += actual_rec_size;
248+
}
249+
250+
/* try with corrected func info records */
251+
attr.func_info = ptr_to_u64(finfo);
252+
attr.func_info_rec_size = load_attr->func_info_rec_size;
253+
254+
fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
255+
256+
if (fd >= 0 || !log_buf || !log_buf_sz)
257+
goto done;
258+
}
259+
219260
/* Try again with log */
220261
attr.log_buf = ptr_to_u64(log_buf);
221262
attr.log_size = log_buf_sz;
222263
attr.log_level = 1;
223264
log_buf[0] = 0;
224-
return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
265+
fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
266+
done:
267+
free(finfo);
268+
return fd;
225269
}
226270

227271
int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,

tools/lib/bpf/btf.c

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ struct btf {
3737
int fd;
3838
};
3939

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+
4052
static int btf_add_type(struct btf *btf, struct btf_type *t)
4153
{
4254
if (btf->types_size - btf->nr_types < 2) {
@@ -397,3 +409,265 @@ const char *btf__name_by_offset(const struct btf *btf, __u32 offset)
397409
else
398410
return NULL;
399411
}
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

Comments
 (0)