Commit 37c7b1ca authored by Daniel Borkmann's avatar Daniel Borkmann

Merge branch 'bpf-btf-type-fixes'

Yonghong Song says:

====================
Commit 69b693f0 ("bpf: btf: Introduce BPF Type Format (BTF)")
introduced BTF, a debug info format for BTF.

The original design has a couple of issues though.
First, the bitfield size is only encoded in int type.
If the struct member bitfield type is enum, pahole ([1])
or llvm is forced to replace enum with int type. As a result, the original
type information gets lost.

Second, the original BTF design does not envision the possibility of
BTF=>header_file conversion ([2]), hence does not encode "struct" or
"union" info for a forward type. Such information is necessary to
convert BTF to a header file.

This patch set fixed the issue by introducing kind_flag, using one bit
in type->info. When kind_flag, the struct/union btf_member->offset
will encode both bitfield_size and bit_offset, covering both
int and enum base types. The kind_flag is also used to indicate whether
the forward type is a union (when set) or a struct.

Patch #1 refactors function btf_int_bits_seq_show() so Patch #2
can reuse part of the function.
Patch #2 implemented kind_flag support for struct/union/fwd types.
Patch #3 added kind_flag support for cgroup local storage map pretty print.
Patch #4 syncs kernel uapi btf.h to tools directory.
Patch #5 added unit tests for kind_flag.
Patch #6 added tests for kernel bpffs based pretty print with kind_flag.
Patch #7 refactors function btf_dumper_int_bits() so Patch #8
can reuse part of the function.
Patch #8 added bpftool support of pretty print with kind_flag set.

  [1] https://git.kernel.org/pub/scm/devel/pahole/pahole.git/commit/?id=b18354f64cc215368c3bc0df4a7e5341c55c378c
  [2] https://lwn.net/SubscriberLink/773198/fe3074838f5c3f26/

Change logs:
  v2 -> v3:
    . Relocated comments about bitfield_size/bit_offset interpretation
      of the "offset" field right before the "offset" struct member.
    . Added missing byte alignment checking for non-bitfield enum
      member of a struct with kind_flag set.
    . Added two test cases in unit tests for struct type, kind_flag set,
      non-bitfield int/enum member, not-byte aligned bit offsets.
    . Added comments to help understand there is no overflow for
      total_bits_offset in bpftool function btf_dumper_int_bits().
    . Added explanation of typedef type dumping fix in Patch #8 commit
      message.

  v1 -> v2:
    . If kind_flag is set for a structure, ensure an int member,
      whether it is a bitfield or not, is a regular int type.
    . Added support so cgroup local storage map pretty print
      works with kind_flag.
====================
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parents 6c4fc209 8772c8bc
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/types.h> #include <linux/types.h>
struct btf; struct btf;
struct btf_member;
struct btf_type; struct btf_type;
union bpf_attr; union bpf_attr;
...@@ -46,7 +47,9 @@ void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj, ...@@ -46,7 +47,9 @@ void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj,
struct seq_file *m); struct seq_file *m);
int btf_get_fd_by_id(u32 id); int btf_get_fd_by_id(u32 id);
u32 btf_id(const struct btf *btf); u32 btf_id(const struct btf *btf);
bool btf_type_is_reg_int(const struct btf_type *t, u32 expected_size); bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s,
const struct btf_member *m,
u32 expected_offset, u32 expected_size);
#ifdef CONFIG_BPF_SYSCALL #ifdef CONFIG_BPF_SYSCALL
const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id); const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id);
......
...@@ -34,7 +34,9 @@ struct btf_type { ...@@ -34,7 +34,9 @@ struct btf_type {
* bits 0-15: vlen (e.g. # of struct's members) * bits 0-15: vlen (e.g. # of struct's members)
* bits 16-23: unused * bits 16-23: unused
* bits 24-27: kind (e.g. int, ptr, array...etc) * bits 24-27: kind (e.g. int, ptr, array...etc)
* bits 28-31: unused * bits 28-30: unused
* bit 31: kind_flag, currently used by
* struct, union and fwd
*/ */
__u32 info; __u32 info;
/* "size" is used by INT, ENUM, STRUCT and UNION. /* "size" is used by INT, ENUM, STRUCT and UNION.
...@@ -52,6 +54,7 @@ struct btf_type { ...@@ -52,6 +54,7 @@ struct btf_type {
#define BTF_INFO_KIND(info) (((info) >> 24) & 0x0f) #define BTF_INFO_KIND(info) (((info) >> 24) & 0x0f)
#define BTF_INFO_VLEN(info) ((info) & 0xffff) #define BTF_INFO_VLEN(info) ((info) & 0xffff)
#define BTF_INFO_KFLAG(info) ((info) >> 31)
#define BTF_KIND_UNKN 0 /* Unknown */ #define BTF_KIND_UNKN 0 /* Unknown */
#define BTF_KIND_INT 1 /* Integer */ #define BTF_KIND_INT 1 /* Integer */
...@@ -110,9 +113,22 @@ struct btf_array { ...@@ -110,9 +113,22 @@ struct btf_array {
struct btf_member { struct btf_member {
__u32 name_off; __u32 name_off;
__u32 type; __u32 type;
__u32 offset; /* offset in bits */ /* If the type info kind_flag is set, the btf_member offset
* contains both member bitfield size and bit offset. The
* bitfield size is set for bitfield members. If the type
* info kind_flag is not set, the offset contains only bit
* offset.
*/
__u32 offset;
}; };
/* If the struct/union type info kind_flag is set, the
* following two macros are used to access bitfield_size
* and bit_offset from btf_member.offset.
*/
#define BTF_MEMBER_BITFIELD_SIZE(val) ((val) >> 24)
#define BTF_MEMBER_BIT_OFFSET(val) ((val) & 0xffffff)
/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param". /* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
* The exact number of btf_param is stored in the vlen (of the * The exact number of btf_param is stored in the vlen (of the
* info in "struct btf_type"). * info in "struct btf_type").
......
This diff is collapsed.
...@@ -315,9 +315,8 @@ static int cgroup_storage_check_btf(const struct bpf_map *map, ...@@ -315,9 +315,8 @@ static int cgroup_storage_check_btf(const struct bpf_map *map,
const struct btf_type *key_type, const struct btf_type *key_type,
const struct btf_type *value_type) const struct btf_type *value_type)
{ {
const struct btf_type *t;
struct btf_member *m; struct btf_member *m;
u32 id, size; u32 offset, size;
/* Key is expected to be of struct bpf_cgroup_storage_key type, /* Key is expected to be of struct bpf_cgroup_storage_key type,
* which is: * which is:
...@@ -338,25 +337,17 @@ static int cgroup_storage_check_btf(const struct bpf_map *map, ...@@ -338,25 +337,17 @@ static int cgroup_storage_check_btf(const struct bpf_map *map,
* The first field must be a 64 bit integer at 0 offset. * The first field must be a 64 bit integer at 0 offset.
*/ */
m = (struct btf_member *)(key_type + 1); m = (struct btf_member *)(key_type + 1);
if (m->offset)
return -EINVAL;
id = m->type;
t = btf_type_id_size(btf, &id, NULL);
size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, cgroup_inode_id); size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, cgroup_inode_id);
if (!t || !btf_type_is_reg_int(t, size)) if (!btf_member_is_reg_int(btf, key_type, m, 0, size))
return -EINVAL; return -EINVAL;
/* /*
* The second field must be a 32 bit integer at 64 bit offset. * The second field must be a 32 bit integer at 64 bit offset.
*/ */
m++; m++;
if (m->offset != offsetof(struct bpf_cgroup_storage_key, attach_type) * offset = offsetof(struct bpf_cgroup_storage_key, attach_type);
BITS_PER_BYTE)
return -EINVAL;
id = m->type;
t = btf_type_id_size(btf, &id, NULL);
size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, attach_type); size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, attach_type);
if (!t || !btf_type_is_reg_int(t, size)) if (!btf_member_is_reg_int(btf, key_type, m, offset, size))
return -EINVAL; return -EINVAL;
return 0; return 0;
......
...@@ -73,20 +73,17 @@ static int btf_dumper_array(const struct btf_dumper *d, __u32 type_id, ...@@ -73,20 +73,17 @@ static int btf_dumper_array(const struct btf_dumper *d, __u32 type_id,
return ret; return ret;
} }
static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset, static void btf_dumper_bitfield(__u32 nr_bits, __u8 bit_offset,
const void *data, json_writer_t *jw, const void *data, json_writer_t *jw,
bool is_plain_text) bool is_plain_text)
{ {
int left_shift_bits, right_shift_bits; int left_shift_bits, right_shift_bits;
int nr_bits = BTF_INT_BITS(int_type);
int total_bits_offset;
int bytes_to_copy; int bytes_to_copy;
int bits_to_copy; int bits_to_copy;
__u64 print_num; __u64 print_num;
total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type); data += BITS_ROUNDDOWN_BYTES(bit_offset);
data += BITS_ROUNDDOWN_BYTES(total_bits_offset); bit_offset = BITS_PER_BYTE_MASKED(bit_offset);
bit_offset = BITS_PER_BYTE_MASKED(total_bits_offset);
bits_to_copy = bit_offset + nr_bits; bits_to_copy = bit_offset + nr_bits;
bytes_to_copy = BITS_ROUNDUP_BYTES(bits_to_copy); bytes_to_copy = BITS_ROUNDUP_BYTES(bits_to_copy);
...@@ -109,6 +106,22 @@ static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset, ...@@ -109,6 +106,22 @@ static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
jsonw_printf(jw, "%llu", print_num); jsonw_printf(jw, "%llu", print_num);
} }
static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
const void *data, json_writer_t *jw,
bool is_plain_text)
{
int nr_bits = BTF_INT_BITS(int_type);
int total_bits_offset;
/* bits_offset is at most 7.
* BTF_INT_OFFSET() cannot exceed 64 bits.
*/
total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type);
btf_dumper_bitfield(nr_bits, total_bits_offset, data, jw,
is_plain_text);
}
static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset, static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset,
const void *data, json_writer_t *jw, const void *data, json_writer_t *jw,
bool is_plain_text) bool is_plain_text)
...@@ -180,6 +193,7 @@ static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id, ...@@ -180,6 +193,7 @@ static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id,
const struct btf_type *t; const struct btf_type *t;
struct btf_member *m; struct btf_member *m;
const void *data_off; const void *data_off;
int kind_flag;
int ret = 0; int ret = 0;
int i, vlen; int i, vlen;
...@@ -187,19 +201,33 @@ static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id, ...@@ -187,19 +201,33 @@ static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id,
if (!t) if (!t)
return -EINVAL; return -EINVAL;
kind_flag = BTF_INFO_KFLAG(t->info);
vlen = BTF_INFO_VLEN(t->info); vlen = BTF_INFO_VLEN(t->info);
jsonw_start_object(d->jw); jsonw_start_object(d->jw);
m = (struct btf_member *)(t + 1); m = (struct btf_member *)(t + 1);
for (i = 0; i < vlen; i++) { for (i = 0; i < vlen; i++) {
data_off = data + BITS_ROUNDDOWN_BYTES(m[i].offset); __u32 bit_offset = m[i].offset;
__u32 bitfield_size = 0;
if (kind_flag) {
bitfield_size = BTF_MEMBER_BITFIELD_SIZE(bit_offset);
bit_offset = BTF_MEMBER_BIT_OFFSET(bit_offset);
}
jsonw_name(d->jw, btf__name_by_offset(d->btf, m[i].name_off)); jsonw_name(d->jw, btf__name_by_offset(d->btf, m[i].name_off));
if (bitfield_size) {
btf_dumper_bitfield(bitfield_size, bit_offset,
data, d->jw, d->is_plain_text);
} else {
data_off = data + BITS_ROUNDDOWN_BYTES(bit_offset);
ret = btf_dumper_do_type(d, m[i].type, ret = btf_dumper_do_type(d, m[i].type,
BITS_PER_BYTE_MASKED(m[i].offset), BITS_PER_BYTE_MASKED(bit_offset),
data_off); data_off);
if (ret) if (ret)
break; break;
} }
}
jsonw_end_object(d->jw); jsonw_end_object(d->jw);
...@@ -285,6 +313,7 @@ static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id, ...@@ -285,6 +313,7 @@ static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,
switch (BTF_INFO_KIND(t->info)) { switch (BTF_INFO_KIND(t->info)) {
case BTF_KIND_INT: case BTF_KIND_INT:
case BTF_KIND_TYPEDEF:
BTF_PRINT_ARG("%s ", btf__name_by_offset(btf, t->name_off)); BTF_PRINT_ARG("%s ", btf__name_by_offset(btf, t->name_off));
break; break;
case BTF_KIND_STRUCT: case BTF_KIND_STRUCT:
...@@ -308,10 +337,11 @@ static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id, ...@@ -308,10 +337,11 @@ static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,
BTF_PRINT_TYPE(t->type); BTF_PRINT_TYPE(t->type);
BTF_PRINT_ARG("* "); BTF_PRINT_ARG("* ");
break; break;
case BTF_KIND_UNKN:
case BTF_KIND_FWD: case BTF_KIND_FWD:
case BTF_KIND_TYPEDEF: BTF_PRINT_ARG("%s %s ",
return -1; BTF_INFO_KFLAG(t->info) ? "union" : "struct",
btf__name_by_offset(btf, t->name_off));
break;
case BTF_KIND_VOLATILE: case BTF_KIND_VOLATILE:
BTF_PRINT_ARG("volatile "); BTF_PRINT_ARG("volatile ");
BTF_PRINT_TYPE(t->type); BTF_PRINT_TYPE(t->type);
...@@ -335,6 +365,7 @@ static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id, ...@@ -335,6 +365,7 @@ static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,
if (pos == -1) if (pos == -1)
return -1; return -1;
break; break;
case BTF_KIND_UNKN:
default: default:
return -1; return -1;
} }
......
...@@ -34,7 +34,9 @@ struct btf_type { ...@@ -34,7 +34,9 @@ struct btf_type {
* bits 0-15: vlen (e.g. # of struct's members) * bits 0-15: vlen (e.g. # of struct's members)
* bits 16-23: unused * bits 16-23: unused
* bits 24-27: kind (e.g. int, ptr, array...etc) * bits 24-27: kind (e.g. int, ptr, array...etc)
* bits 28-31: unused * bits 28-30: unused
* bit 31: kind_flag, currently used by
* struct, union and fwd
*/ */
__u32 info; __u32 info;
/* "size" is used by INT, ENUM, STRUCT and UNION. /* "size" is used by INT, ENUM, STRUCT and UNION.
...@@ -52,6 +54,7 @@ struct btf_type { ...@@ -52,6 +54,7 @@ struct btf_type {
#define BTF_INFO_KIND(info) (((info) >> 24) & 0x0f) #define BTF_INFO_KIND(info) (((info) >> 24) & 0x0f)
#define BTF_INFO_VLEN(info) ((info) & 0xffff) #define BTF_INFO_VLEN(info) ((info) & 0xffff)
#define BTF_INFO_KFLAG(info) ((info) >> 31)
#define BTF_KIND_UNKN 0 /* Unknown */ #define BTF_KIND_UNKN 0 /* Unknown */
#define BTF_KIND_INT 1 /* Integer */ #define BTF_KIND_INT 1 /* Integer */
...@@ -110,9 +113,22 @@ struct btf_array { ...@@ -110,9 +113,22 @@ struct btf_array {
struct btf_member { struct btf_member {
__u32 name_off; __u32 name_off;
__u32 type; __u32 type;
__u32 offset; /* offset in bits */ /* If the type info kind_flag is set, the btf_member offset
* contains both member bitfield size and bit offset. The
* bitfield size is set for bitfield members. If the type
* info kind_flag is not set, the offset contains only bit
* offset.
*/
__u32 offset;
}; };
/* If the struct/union type info kind_flag is set, the
* following two macros are used to access bitfield_size
* and bit_offset from btf_member.offset.
*/
#define BTF_MEMBER_BITFIELD_SIZE(val) ((val) >> 24)
#define BTF_MEMBER_BIT_OFFSET(val) ((val) & 0xffffff)
/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param". /* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
* The exact number of btf_param is stored in the vlen (of the * The exact number of btf_param is stored in the vlen (of the
* info in "struct btf_type"). * info in "struct btf_type").
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment