From: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx> We get the size for btf->raw_size from the ELF section where it is or from the size of a detached BTF, check if there is extra data at the end and avoid wasting space, when not using mmap. This can happen if we generate BTF for all .o files and it then gets combined by the linker, which is the default action when finding unhandled sections with the same name in multiple .o files. For instance: root@x1:~/bla# bpftool -d btf dump file ~acme/btf2btf/vmlinux | wc -l libbpf: BTF raw_size chopped from 380507209 to 238545 12084 root@x1:~/bla# The above is one such file, where that vmlinux .BTF section has lots of .BTF files combined by the linker. A deduplicated one, generated by pahole + libbpf when converting from DWARF has way more stuff in it and the raw_size matches the ELF size: root@x1:~/bla# bpftool -d btf dump file /sys/kernel/btf/vmlinux |wc -l 355927 root@x1:~/bla# Cc: Alexei Starovoitov <alexei.starovoitov@xxxxxxxxx> Cc: Andrii Nakryiko <andrii.nakryiko@xxxxxxxxx> Cc: Jiri Olsa <jolsa@xxxxxxxxxx> Cc: "Jose E. Marchesi" <jose.marchesi@xxxxxxxxxx> Cc: Namhyung Kim <namhyung@xxxxxxxxxx> Cc: Nick Alcock <nick.alcock@xxxxxxxxxx> Cc: Yonghong Song <yonghong.song@xxxxxxxxx> Signed-off-by: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx> --- tools/lib/bpf/btf.c | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 9bacd4dddff366bf..ee45d461d53bea9a 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -124,6 +124,9 @@ struct btf { /* whether raw_data is a (read-only) mmap */ bool raw_data_is_mmap; + /* Wheter there was more data after the end of strings */ + bool extra_raw_data; + /* BTF object FD, if loaded into kernel */ int fd; @@ -225,10 +228,9 @@ static void btf_bswap_hdr(struct btf_header *h) h->str_len = bswap_32(h->str_len); } -static int btf_parse_hdr(struct btf *btf) +static int btf_parse_hdr(struct btf *btf, struct btf_header *hdr) { - struct btf_header *hdr = btf->hdr; - __u32 meta_left; + __u32 meta_left, raw_size; if (btf->raw_size < sizeof(struct btf_header)) { pr_debug("BTF header not found\n"); @@ -271,6 +273,18 @@ static int btf_parse_hdr(struct btf *btf) return -EINVAL; } + /* If there is more data after the strings, it will not be used, + * so we might as well trim here and don't waste memory. + * This paves the way for a BTF archive, created by default + * by the linker when finding .BTF in multiple .o files. + */ + raw_size = sizeof(*hdr) + hdr->str_off + hdr->str_len; + if (raw_size != btf->raw_size) { + pr_debug("BTF raw_size chopped from %u to %u\n", btf->raw_size, raw_size); + btf->raw_size = raw_size; + btf->extra_raw_data = true; + } + return 0; } @@ -1047,6 +1061,7 @@ struct btf *btf__new_empty_split(struct btf *base_btf) static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf, bool is_mmap) { + struct btf_header hdr; struct btf *btf; int err; @@ -1065,24 +1080,31 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf, b btf->start_str_off = base_btf->hdr->str_len; } + /* We still don't know if this is an archive, i.e. if 'size' is + * the raw_size of a BTF or the sum of all BTFs in an archive, + * it'll be adjusted when we parse the header. + */ + btf->raw_size = size; + memcpy(&hdr, data, sizeof(hdr)); + + err = btf_parse_hdr(btf, &hdr); + if (err) + goto done; + if (is_mmap) { btf->raw_data = (void *)data; btf->raw_data_is_mmap = true; } else { - btf->raw_data = malloc(size); + btf->raw_data = malloc(btf->raw_size); if (!btf->raw_data) { err = -ENOMEM; goto done; } - memcpy(btf->raw_data, data, size); + memcpy(btf->raw_data, data, btf->raw_size); } - btf->raw_size = size; - btf->hdr = btf->raw_data; - err = btf_parse_hdr(btf); - if (err) - goto done; + memcpy(btf->hdr, &hdr, sizeof(hdr)); btf->strs_data = btf->raw_data + btf->hdr->hdr_len + btf->hdr->str_off; btf->types_data = btf->raw_data + btf->hdr->hdr_len + btf->hdr->type_off; -- 2.50.1