BPF List
 help / color / mirror / Atom feed
From: Eduard Zingerman <eddyz87@gmail.com>
To: bpf@vger.kernel.org, ast@kernel.org
Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev,
	kernel-team@fb.com, yonghong.song@linux.dev,
	quentin@isovalent.com, alan.maguire@oracle.com,
	Eduard Zingerman <eddyz87@gmail.com>
Subject: [RFC v3 2/3] bpftool: add attribute preserve_static_offset for context types
Date: Wed, 20 Dec 2023 15:34:10 +0200	[thread overview]
Message-ID: <20231220133411.22978-3-eddyz87@gmail.com> (raw)
In-Reply-To: <20231220133411.22978-1-eddyz87@gmail.com>

When printing vmlinux.h, emit attribute preserve_static_offset [0]
for types that have associated BTF decl tag T with value
"preserve_static_offset".

To avoid hacking libbpf dump logic emit forward declarations annotated
with attribute. Such forward declarations have to come before
structure definitions.

Only emit such forward declarations when context types are present in
target BTF (identified by name).

C language standard wording in section "6.7.2.1 Structure and union
specifiers" [1] is vague, but example in 6.7.2.1.21 explicitly allows
such notation, and it matches clang behavior.

Here is how 'bpftool btf gen ... format c' looks after this change:

    #ifndef __VMLINUX_H__
    #define __VMLINUX_H__

    #if !defined(BPF_NO_PRESERVE_STATIC_OFFSET) && \
        __has_attribute(preserve_static_offset)
    #pragma clang attribute push \
              (__attribute__((preserve_static_offset)), apply_to = record)

    struct bpf_cgroup_dev_ctx;
    ...

    #pragma clang attribute pop
    #endif

    ... rest of the output unchanged ...

This is a follow up for discussion in thread [2].

[0] https://clang.llvm.org/docs/AttributeReference.html#preserve-static-offset
[1] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3088.pdf
[2] https://lore.kernel.org/bpf/fce6188a-6ccc-4b92-9aa7-9ee18b2f2fa1@isovalent.com/

Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
---
 tools/bpf/bpftool/btf.c | 135 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 135 insertions(+)

diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index 91fcb75babe3..feded48ddd76 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -460,6 +460,136 @@ static void __printf(2, 0) btf_dump_printf(void *ctx,
 	vfprintf(stdout, fmt, args);
 }
 
+/* Recursively walk all dependencies of 'id' and mark those as true in
+ * array 'deps'. The goal is to find all types that would be printed by
+ * btf_dump if 'id' is dumped.
+ */
+static void mark_dependencies(const struct btf *btf, __u32 id, bool *deps)
+{
+	const struct btf_param *params;
+	const struct btf_array *arr;
+	const struct btf_type *t;
+	struct btf_member *m;
+	__u16 vlen, i;
+
+	if (id == 0 || deps[id])
+		return;
+
+	deps[id] = true;
+	t = btf__type_by_id(btf, id);
+	if (!t)
+		return;
+
+	switch (btf_kind(t)) {
+	case BTF_KIND_PTR:
+	case BTF_KIND_TYPEDEF:
+	case BTF_KIND_VOLATILE:
+	case BTF_KIND_CONST:
+	case BTF_KIND_RESTRICT:
+	case BTF_KIND_TYPE_TAG:
+	case BTF_KIND_DECL_TAG:
+	case BTF_KIND_FUNC:
+		mark_dependencies(btf, t->type, deps);
+		break;
+	case BTF_KIND_STRUCT:
+	case BTF_KIND_UNION:
+		vlen = btf_vlen(t);
+		m = btf_members(t);
+		for (i = 0; i < vlen; ++i)
+			mark_dependencies(btf, m[i].type, deps);
+		break;
+	case BTF_KIND_ARRAY:
+		arr = btf_array(t);
+		mark_dependencies(btf, arr->type, deps);
+		break;
+	case BTF_KIND_FUNC_PROTO:
+		vlen = btf_vlen(t);
+		params = btf_params(t);
+		mark_dependencies(btf, t->type, deps);
+		for (i = 0; i < vlen; ++i)
+			mark_dependencies(btf, params[i].type, deps);
+		break;
+	default:
+		/* ignore */
+		break;
+	}
+}
+
+/* Iterate all types in 'btf', if there are BTF_DECL_TAG records R
+ * with "preserve_static_offset" tag - emit a forward declaration
+ * for R->type annotated with preserve_static_offset attribute [0].
+ *
+ * If root_type_ids/root_type_cnt is specified, filter generated declarations
+ * to only include root_type_ids and corresponding dependencies.
+ *
+ * [0] https://clang.llvm.org/docs/AttributeReference.html#preserve-static-offset
+ */
+static int emit_static_offset_protos(const struct btf *btf,
+				     __u32 *root_type_ids, int root_type_cnt)
+{
+	bool *root_type_deps = NULL;
+	bool first = true;
+	__u32 i, id, cnt;
+
+	cnt = btf__type_cnt(btf);
+	if (root_type_cnt) {
+		root_type_deps = calloc(cnt, sizeof(*root_type_deps));
+		if (!root_type_deps)
+			return -ENOMEM;
+
+		for (i = 0; i < (__u32)root_type_cnt; ++i)
+			mark_dependencies(btf, root_type_ids[i], root_type_deps);
+	}
+
+	for (id = 1; id < cnt; ++id) {
+		const struct btf_type *t, *s;
+		const char *name, *tag;
+
+		t = btf__type_by_id(btf, id);
+		if (!t)
+			continue;
+
+		if (!btf_is_decl_tag(t))
+			continue;
+
+		tag = btf__name_by_offset(btf, t->name_off);
+		if (strcmp(tag, "preserve_static_offset") != 0)
+			continue;
+
+		if (root_type_deps && !root_type_deps[t->type])
+			continue;
+
+		s = btf__type_by_id(btf, t->type);
+		if (!s)
+			continue;
+
+		if (!btf_is_struct(s) && !btf_is_union(s))
+			continue;
+
+		name = btf__name_by_offset(btf, s->name_off);
+		if (!name)
+			continue;
+
+		if (first) {
+			first = false;
+			printf("#if !defined(BPF_NO_PRESERVE_STATIC_OFFSET) && __has_attribute(preserve_static_offset)\n");
+			printf("#pragma clang attribute push (__attribute__((preserve_static_offset)), apply_to = record)\n");
+			printf("\n");
+		}
+
+		printf("struct %s;\n", name);
+	}
+
+	if (!first) {
+		printf("\n");
+		printf("#pragma clang attribute pop\n");
+		printf("#endif /* BPF_NO_PRESERVE_STATIC_OFFSET */\n\n");
+	}
+
+	free(root_type_deps);
+	return 0;
+}
+
 static int dump_btf_c(const struct btf *btf,
 		      __u32 *root_type_ids, int root_type_cnt)
 {
@@ -473,6 +603,11 @@ static int dump_btf_c(const struct btf *btf,
 	printf("#ifndef __VMLINUX_H__\n");
 	printf("#define __VMLINUX_H__\n");
 	printf("\n");
+
+	err = emit_static_offset_protos(btf, root_type_ids, root_type_cnt);
+	if (err)
+		goto done;
+
 	printf("#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n");
 	printf("#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)\n");
 	printf("#endif\n\n");
-- 
2.42.1


  parent reply	other threads:[~2023-12-20 13:34 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-12-20 13:34 [RFC v3 0/3] use preserve_static_offset in bpf uapi headers Eduard Zingerman
2023-12-20 13:34 ` [RFC v3 1/3] bpf: Mark virtual BPF context structures as preserve_static_offset Eduard Zingerman
2023-12-20 13:34 ` Eduard Zingerman [this message]
2023-12-20 13:34 ` [RFC v3 3/3] selftests/bpf: verify bpftool emits preserve_static_offset Eduard Zingerman
2023-12-20 19:20 ` [RFC v3 0/3] use preserve_static_offset in bpf uapi headers Alexei Starovoitov
2023-12-20 20:19   ` Eduard Zingerman
2024-01-03 13:06     ` Quentin Monnet

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231220133411.22978-3-eddyz87@gmail.com \
    --to=eddyz87@gmail.com \
    --cc=alan.maguire@oracle.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=kernel-team@fb.com \
    --cc=martin.lau@linux.dev \
    --cc=quentin@isovalent.com \
    --cc=yonghong.song@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox