dwarves.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH dwarves 0/1] Support for new btf_type_tag encoding
@ 2023-03-13  2:17 Eduard Zingerman
  2023-03-13  2:17 ` [PATCH dwarves 1/1] dwarf_loader: Support for btf:type_tag Eduard Zingerman
  2023-03-13 20:32 ` [PATCH dwarves 0/1] Support for new btf_type_tag encoding Arnaldo Carvalho de Melo
  0 siblings, 2 replies; 6+ messages in thread
From: Eduard Zingerman @ 2023-03-13  2:17 UTC (permalink / raw)
  To: dwarves, arnaldo.melo
  Cc: bpf, kernel-team, ast, daniel, andrii, yhs, jose.marchesi,
	david.faust, Eduard Zingerman

In recent discussion in BPF mailing list ([1]) participants agreed to
add a new DWARF representation for "btf_type_tag" annotations.

Existing representation is DW_TAG_LLVM_annotation object attached as a
child to a DW_TAG_pointer_type. It means that "btf_type_tag"
annotation is attached to a pointee type.

New representation is DW_TAG_LLVM_annotation object attached as a
child to *any* type. It means that "btf_type_tag" annotation is
attached to the parent type.

Here is an example:

    int __attribute__((btf_type_tag("tag1"))) *g;

And corresponding DWARF:

    0x0000001e:   DW_TAG_variable
                    DW_AT_name      ("g")
                    DW_AT_type      (0x00000029 "int *")

    0x00000029:   DW_TAG_pointer_type
                    DW_AT_type      (0x00000032 "int")

    0x0000002e:     DW_TAG_LLVM_annotation
                      DW_AT_name    ("btf_type_tag")
                      DW_AT_const_value     ("tag1")

    0x00000032:   DW_TAG_base_type
                    DW_AT_name      ("int")


This patch adds logic necessary to handle such annotations in the
pahole tool. Examples like below are supported:

  #define __tag(val) __attribute__((btf_type_tag("__" #val)))

  struct      alpha {};
  union       bravo {};
  enum        charlie { X };
  typedef int delta;

  struct echo {
    int          * __tag(a)  a;
    int            __tag(b) *b;
    int            __tag(c)  c;
    void           __tag(d) *d;
    void           __tag(e) *e;
    struct alpha   __tag(f)  f;
    union  bravo   __tag(g)  g;
    enum   charlie __tag(h)  h;
    delta          __tag(i)  i;
    int __tag(j_result) (__tag(j) *j)(int __tag(j_param));
  } g;

Implementation details
----------------------

Although this was not discussed in the mailing list, the proposed
implementation acts in a following way (for compatibility reasons):
- both forms could be present in the debug info;
- if any annotations corresponding to the new form are present in the
  debug info, annotations corresponding to the old form are ignored.

Because the new form of type tags could be applied to any type it is
somewhat invasive from implementation point of view:
- Types `struct btf_type_tag_type` and `struct llvm_annotation` are
  consolidated as a single type `struct llvm_annotation`, in order to
  reside in a single `annots` list associated with struct/union/enum
  or typedef.
- The `annots` list, used to hold references to annotation objects,
  is put directly in `struct tag`.
- At the load phase it is not yet known whether new or old form is
  used for type tags encoding, so `struct llvm_annotation` objects are
  not added to the types table.
- The recode phase now consists of several steps:
  - depending on the results of the load phase, `struct llvm_annotation`
    objects corresponding to either old or new representations are
    added to the types table;
  - `tag__recode_dwarf_type()` is executed as usual, but it also
    collects information about type tags;
  - references to types that have type tag annotations are updated to
    point to the first annotation object.

Corresponding clang changes are tracked in [6].

Testing
-------

To verify the changes I used the following:
- Tools:
  - "LLVM-main"   :: LLVM at revision [3];
  - "LLVM-new"    :: LLVM at revision [3] with patches [6] applied;
  - "gcc"         :: GCC version 11.3 (no support for btf_type_tag annotations);
  - "pahole-next" :: dwarves at revision [4];
  - "pahole-new"  :: dwarves at revision [4] + this patch,
  - "kernel"      :: Linux Kernel bpf-next branch at revision [5]
- test cases:
  - kernel build;
  - kernel BPF test cases build, BPF tests execution
    (test_verifier, test_progs, test_progs-no_alu32, test_maps);
  - btfdiff script (suggested by Arnaldo, [2]).
- tool combinations (kernel compiler / clang for BPF tests / pahole version):
  - LLVM-main / LLVM-main / pahole-new
    - kernel build : ok
    - bpf tests    : ok
    - btfdiff      : ok (modulo diff #1, see below)
  - gcc       / LLVM-main / pahole-new
    - kernel build : ok
    - bpf tests    : ok
    - btfdiff      : ok
  - LLVM-new  / LLVM-new  / pahole-next
    - kernel build : ok with warnings (see warn #1 below)
    - bpf tests    : ok
    - btfdiff      : fails (see diff #2 below)
  - LLVM-new  / LLVM-new  / pahole-new
    - kernel build : ok
    - bpf tests    : ok
    - btfdiff      : ok (modulo diff #1, see below)
  - gcc       / LLVM-new  / pahole-new
    - kernel build : ok
    - bpf tests    : ok
    - btfdiff      : ok

Note on BPF tests: test case `verif_scale_loop6` fails for all
configurations above and 'LLVM-main / LLVM-main / pahole-next',
thus I consider this issue as unrelated.

Diff #1: Difference in flexible printing, several occurrences as below:

  @ -10531,7 +10531,7 @ struct bpf_cand_cache {
          struct {
                  const struct btf  * btf;                 /*    16     8 */
                  u32                id;                   /*    24     4 */
  -       } cands[0]; /*    16     0 */
  +       } cands[]; /*    16     0 */
   
          /* size: 16, cachelines: 1, members: 5 */
          /* last cacheline: 16 bytes */

Diff #2: pahole-next does not know how to print type name for types
with type tags, when reading from BTF:

  @ -72998,8 +73022,8 @ struct sock {
          /* --- cacheline 19 boundary (1216 bytes) --- */
          int                        (*sk_backlog_rcv)(struct sock *, struct sk_buff *); /*  1216     8 */
          void                       (*sk_destruct)(struct sock *); /*  1224     8 */
  -       rcu *                      sk_reuseport_cb;      /*  1232     8 */
  -       rcu *                      sk_bpf_storage;       /*  1240     8 */
  +       <ERROR                    > sk_reuseport_cb;     /*  1232     8 */
  +       <ERROR                    > sk_bpf_storage;      /*  1240     8 */

(Also DWARF names refer to TYPE_TAG, not actual type name, fixed in pahole-new).

Warn #1: pahole-next complains about unexpected child tags generated
by clang, e.g.:

  die__create_new_tag: unspecified_type WITH children!
  die__create_new_base_type: DW_TAG_base_type WITH children!


Performance impact
------------------

The update to `struct tag` might raise concerns regarding memory
usage, additional steps in recode phase might raise concerns regarding
execution time. Below is statistics collected for Kernel BTF
generation.

LLVM-new / LLVM-new / pahole-new:

$ /usr/bin/time -v pahole -J --btf_gen_floats -j --lang_exclude=rust .tmp_vmlinux.btf
    ...
	User time (seconds): 22.29
	System time (seconds): 0.47
	Percent of CPU this job got: 483%
    ...
	Maximum resident set size (kbytes): 714524
    ...

LLVM-new / LLVM-new / pahole-next:

$ /usr/bin/time -v pahole -J --btf_gen_floats -j --lang_exclude=rust .tmp_vmlinux.btf
    ...
	User time (seconds): 20.96
	System time (seconds): 0.44
	Percent of CPU this job got: 473%
    ...
	Maximum resident set size (kbytes): 700848
    ...

Links & revisions
-----------------

[1] Mailing list discussion regarding `btf:type_tag`
    https://lore.kernel.org/bpf/87r0w9jjoq.fsf@oracle.com/
[2] Suggestion to use btfdiff
    https://lore.kernel.org/dwarves/ZAKpZGSHTvsS4r8E@kernel.org/T/#mddbfe661e339485fb2b0e706b31329b46bf61bda
[3] f759275c1c8e ("[AMDGPU] Regenerate sdwa-peephole.ll")
[4] a9498899109d ("dwarf_loader: Support for btf:type_tag")
[5] 49b5300f1f8f ("Merge branch 'Support stashing local kptrs with bpf_kptr_xchg'")
[6] LLVM changes to generate btf:type_tag, revisions stack:
    https://reviews.llvm.org/D143966
    https://reviews.llvm.org/D143967
    https://reviews.llvm.org/D145891

Eduard Zingerman (1):
  dwarf_loader: Support for btf:type_tag

 btf_encoder.c     |  13 +-
 btf_loader.c      |  15 +-
 dwarf_loader.c    | 763 +++++++++++++++++++++++++++++++++++++---------
 dwarves.c         |   1 +
 dwarves.h         |  68 +++--
 dwarves_fprintf.c |  13 +
 6 files changed, 693 insertions(+), 180 deletions(-)

-- 
2.39.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH dwarves 1/1] dwarf_loader: Support for btf:type_tag
  2023-03-13  2:17 [PATCH dwarves 0/1] Support for new btf_type_tag encoding Eduard Zingerman
@ 2023-03-13  2:17 ` Eduard Zingerman
  2023-03-14 10:30   ` Alan Maguire
  2023-03-13 20:32 ` [PATCH dwarves 0/1] Support for new btf_type_tag encoding Arnaldo Carvalho de Melo
  1 sibling, 1 reply; 6+ messages in thread
From: Eduard Zingerman @ 2023-03-13  2:17 UTC (permalink / raw)
  To: dwarves, arnaldo.melo
  Cc: bpf, kernel-team, ast, daniel, andrii, yhs, jose.marchesi,
	david.faust, Eduard Zingerman

"btf:type_tag" is an DW_TAG_LLVM_annotation object that encodes
btf_type_tag attributes in DWARF. Contrary to existing "btf_type_tag"
it allows to associate such attributes with non-pointer types.
When "btf:type_tag" is attached to a type it applies to this type.

For example the following C code:

    struct echo {
      int __attribute__((btf_type_tag("__c"))) c;
    }

Produces the following DWARF:

0x29:   DW_TAG_structure_type
          DW_AT_name      ("echo")

0x40:     DW_TAG_member
            DW_AT_name    ("c")
            DW_AT_type    (0x8c "int")

0x8c:   DW_TAG_base_type
          DW_AT_name      ("int")
          DW_AT_encoding  (DW_ATE_signed)
          DW_AT_byte_size (0x04)

0x90:     DW_TAG_LLVM_annotation
            DW_AT_name    ("btf:type_tag")
            DW_AT_const_value     ("__c")

Meaning that type 0x8c is an `int` with type tag `__c`.
Corresponding BTF looks as follows:

[1] STRUCT 'echo'
        ...
        'c' type_id=8 bits_offset=128
[4] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
[8] TYPE_TAG '__c' type_id=4

Void pointers with type tag annotations are represented as objects
of unspecified type with name "void", here is an example:

  struct st {
    void __attribute__((btf_type_tag("__d"))) *d;
  }

And corresponding DWARF:

  0x29:   DW_TAG_structure_type
            DW_AT_name      ("st")

  0x49:     DW_TAG_member
              DW_AT_name    ("d")
              DW_AT_type    (0xa6 "void *")

  0xa6:   DW_TAG_pointer_type
            DW_AT_type      (0xaf "void")

  0xaf:   DW_TAG_unspecified_type
            DW_AT_name      ("void")

  0xb1:     DW_TAG_LLVM_annotation
              DW_AT_name    ("btf:type_tag")
              DW_AT_const_value     ("__d")

This commit adds support for DW_TAG_LLVM_annotation "btf:type_tag"
attached to the following entities:
- base types;
- arrays;
- pointers;
- structs
- unions;
- enums;
- typedefs.

This is achieved via the following modifications:
- Types `struct btf_type_tag_type` and `struct llvm_annotation` are
  consolidated as a single type `struct llvm_annotation`, in order to
  reside in a single `annots` list associated with struct/union/enum
  or typedef.
- `struct unspecified_type` is added to handle `void *` types annotated
  with `btf:type_tag`.
- `struct tag` is extended with `annots` list.
- DWARF load phase is modified to fill `annots` fields for the above
  mentioned types.
- Recode phase is split in two sub-phases:
  - The existing `tag__recode_dwarf_type()` is executed first;
  - Newly added `update_btf_type_tag_refs()` is executed to update
    `->type` field of each tag if that type refers to an object with
    `btf:type_tag` annotation. The id of the type is replaced by id
    of the type tag.
- When `btf:type_tag` annotations are present in compilation unit,
  all `btf_type_tag` annotations are ignored during BTF dump.

See also:
[1] Mailing list discussion regarding `btf:type_tag`
    https://lore.kernel.org/bpf/87r0w9jjoq.fsf@oracle.com/

Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
---
 btf_encoder.c     |  13 +-
 btf_loader.c      |  15 +-
 dwarf_loader.c    | 763 +++++++++++++++++++++++++++++++++++++---------
 dwarves.c         |   1 +
 dwarves.h         |  68 +++--
 dwarves_fprintf.c |  13 +
 6 files changed, 693 insertions(+), 180 deletions(-)

diff --git a/btf_encoder.c b/btf_encoder.c
index 07a9dc5..b74911b 100644
--- a/btf_encoder.c
+++ b/btf_encoder.c
@@ -899,6 +899,9 @@ static int32_t btf_encoder__add_func(struct btf_encoder *encoder, struct functio
 		return -1;
 	}
 	list_for_each_entry(annot, &fn->annots, node) {
+		if (annot->kind != BTF_DECL_TAG)
+			continue;
+
 		tag_type_id = btf_encoder__add_decl_tag(encoder, annot->value, btf_fn_id,
 							annot->component_idx);
 		if (tag_type_id < 0) {
@@ -1180,7 +1183,7 @@ static int btf_encoder__encode_tag(struct btf_encoder *encoder, struct tag *tag,
 		name = namespace__name(tag__namespace(tag));
 		return btf_encoder__add_ref_type(encoder, BTF_KIND_TYPEDEF, ref_type_id, name, false);
 	case DW_TAG_LLVM_annotation:
-		name = tag__btf_type_tag(tag)->value;
+		name = tag__llvm_annotation(tag)->value;
 		return btf_encoder__add_ref_type(encoder, BTF_KIND_TYPE_TAG, ref_type_id, name, false);
 	case DW_TAG_structure_type:
 	case DW_TAG_union_type:
@@ -1605,6 +1608,9 @@ static int btf_encoder__encode_cu_variables(struct btf_encoder *encoder)
 		}
 
 		list_for_each_entry(annot, &var->annots, node) {
+			if (annot->kind != BTF_DECL_TAG)
+				continue;
+
 			int tag_type_id = btf_encoder__add_decl_tag(encoder, annot->value, id, annot->component_idx);
 			if (tag_type_id < 0) {
 				fprintf(stderr, "error: failed to encode tag '%s' to variable '%s' with component_idx %d\n",
@@ -1798,7 +1804,10 @@ int btf_encoder__encode_cu(struct btf_encoder *encoder, struct cu *cu, struct co
 
 		btf_type_id = encoder->type_id_off + core_id;
 		ns = tag__namespace(pos);
-		list_for_each_entry(annot, &ns->annots, node) {
+		list_for_each_entry(annot, &ns->tag.annots, node) {
+			if (annot->kind != BTF_DECL_TAG)
+				continue;
+
 			tag_type_id = btf_encoder__add_decl_tag(encoder, annot->value, btf_type_id, annot->component_idx);
 			if (tag_type_id < 0) {
 				fprintf(stderr, "error: failed to encode tag '%s' to %s '%s' with component_idx %d\n",
diff --git a/btf_loader.c b/btf_loader.c
index e579323..3fe07d0 100644
--- a/btf_loader.c
+++ b/btf_loader.c
@@ -429,10 +429,11 @@ static int create_new_tag(struct cu *cu, int type, const struct btf_type *tp, ui
 		return -ENOMEM;
 
 	switch (type) {
-	case BTF_KIND_CONST:	tag->tag = DW_TAG_const_type;	 break;
-	case BTF_KIND_PTR:	tag->tag = DW_TAG_pointer_type;  break;
-	case BTF_KIND_RESTRICT:	tag->tag = DW_TAG_restrict_type; break;
-	case BTF_KIND_VOLATILE:	tag->tag = DW_TAG_volatile_type; break;
+	case BTF_KIND_CONST:	tag->tag = DW_TAG_const_type;	   break;
+	case BTF_KIND_PTR:	tag->tag = DW_TAG_pointer_type;    break;
+	case BTF_KIND_RESTRICT:	tag->tag = DW_TAG_restrict_type;   break;
+	case BTF_KIND_VOLATILE:	tag->tag = DW_TAG_volatile_type;   break;
+	case BTF_KIND_TYPE_TAG:	tag->tag = DW_TAG_LLVM_annotation; break;
 	default:
 		free(tag);
 		printf("%s: Unknown type %d\n\n", __func__, type);
@@ -489,6 +490,12 @@ static int btf__load_types(struct btf *btf, struct cu *cu)
 		case BTF_KIND_PTR:
 		case BTF_KIND_CONST:
 		case BTF_KIND_RESTRICT:
+		/* For type tag it's a bit of a lie.
+		 * In DWARF it is encoded as a child tag of whatever type it
+		 * applies to. Here we load it as a standalone tag with a pointer
+		 * to a next type only to have a valid ID in the types table.
+		 */
+		case BTF_KIND_TYPE_TAG:
 			err = create_new_tag(cu, type, type_ptr, type_index);
 			break;
 		case BTF_KIND_UNKN:
diff --git a/dwarf_loader.c b/dwarf_loader.c
index 4efa4e1..7846b94 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -57,6 +57,36 @@
 #define EM_RISCV	243
 #endif
 
+/** struct btf_type_tag_mapping - information about btf type tags attached
+ *  to a particular host type. Each field is a small_id of the dwarf tag.
+ *  This information is used to replace references to host type with
+ *  references to first btf type tag during the recode phase.
+ *
+ *  @host_type_id - type annotated with btf_type_tag annotations
+ *  @first_tag_id - first btf type tag attached to host_type_id
+ *  @last_tag_id  - last btf type tag attached to host_type_id
+ *
+ */
+struct btf_type_tag_mapping {
+	uint32_t host_type_id;
+	uint32_t first_tag_id;
+	uint32_t last_tag_id;
+};
+
+/** struct recode_context - information local to recode phase,
+ *  currently only btf type tag mappings.
+ *
+ *  @mappings     - an array of host id to btf type tag mappings,
+ *                  dynamically enlarged when new mappings are added;
+ *  @nr_allocated - number of elements allocated for @mappings array;
+ *  @nr_entries   - index of the next free entry in the @mappings array.
+ */
+struct recode_context {
+	struct btf_type_tag_mapping *mappings;
+	uint32_t nr_allocated;
+	uint32_t nr_entries;
+};
+
 static pthread_mutex_t libdw__lock = PTHREAD_MUTEX_INITIALIZER;
 
 static uint32_t hashtags__bits = 12;
@@ -112,6 +142,17 @@ static dwarf_off_ref dwarf_tag__spec(struct dwarf_tag *dtag)
 	return *(dwarf_off_ref *)(dtag + 1);
 }
 
+#define cu__tag_not_handled(die) __cu__tag_not_handled(die, __FUNCTION__)
+
+static void __cu__tag_not_handled(Dwarf_Die *die, const char *fn)
+{
+	uint32_t tag = dwarf_tag(die);
+
+	fprintf(stderr, "%s: DW_TAG_%s (%#x) @ <%#llx> not handled!\n",
+		fn, dwarf_tag_name(tag), tag,
+		(unsigned long long)dwarf_dieoffset(die));
+}
+
 static void dwarf_tag__set_spec(struct dwarf_tag *dtag, dwarf_off_ref spec)
 {
 	*(dwarf_off_ref *)(dtag + 1) = spec;
@@ -165,14 +206,21 @@ static struct dwarf_cu *dwarf_cu__new(struct cu *cu)
 	return dwarf_cu;
 }
 
+static void unspecified_type__delete(struct cu *cu, struct unspecified_type *utype);
+
 static void dwarf_cu__delete(struct cu *cu)
 {
 	if (cu == NULL || cu->priv == NULL)
 		return;
 
 	struct dwarf_cu *dcu = cu->priv;
+	struct list_head *pos, *n;
 
 	// dcu->hash_tags & dcu->hash_types are on cu->obstack
+	cu__free(cu, dcu->hash_tags);
+	cu__free(cu, dcu->hash_types);
+	list_for_each_safe(pos, n, &cu->unspecified_types)
+		unspecified_type__delete(cu, container_of(pos, struct unspecified_type, node));
 	cu__free(cu, dcu);
 	cu->priv = NULL;
 }
@@ -519,6 +567,7 @@ static void tag__init(struct tag *tag, struct cu *cu, Dwarf_Die *die)
 	}
 
 	INIT_LIST_HEAD(&tag->node);
+	INIT_LIST_HEAD(&tag->annots);
 }
 
 static struct tag *tag__new(Dwarf_Die *die, struct cu *cu)
@@ -608,7 +657,6 @@ static void namespace__init(struct namespace *namespace, Dwarf_Die *die,
 {
 	tag__init(&namespace->tag, cu, die);
 	INIT_LIST_HEAD(&namespace->tags);
-	INIT_LIST_HEAD(&namespace->annots);
 	namespace->name  = attr_string(die, DW_AT_name, conf);
 	namespace->nr_tags = 0;
 	namespace->shared_tags = 0;
@@ -876,8 +924,40 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b
 	return -ENOMEM;
 }
 
-static int add_llvm_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf,
-			       struct list_head *head)
+static struct llvm_annotation *die__create_new_llvm_annotation(Dwarf_Die *die,
+							       struct cu *cu,
+							       struct conf_load *conf)
+{
+	struct llvm_annotation *tag;
+
+	tag = tag__alloc_with_spec(cu, sizeof(struct llvm_annotation));
+	if (tag == NULL)
+		return NULL;
+
+	tag__init(&tag->tag, cu, die);
+	return tag;
+}
+
+/** Allocate small_id for specified @tag */
+static int cu__assign_tag_id(struct cu *cu, struct tag *tag)
+{
+	struct dwarf_tag *dtag = tag->priv;
+	uint32_t id;
+
+	if (cu__table_add_tag(cu, tag, &id) < 0)
+		return -ENOMEM;
+
+	dtag->small_id = id;
+	cu__hash(cu, tag);
+
+	return 0;
+}
+
+static int add_btf_decl_tag(Dwarf_Die *die,
+			    struct cu *cu,
+			    int component_idx,
+			    struct conf_load *conf,
+			    struct list_head *head)
 {
 	struct llvm_annotation *annot;
 	const char *name;
@@ -890,17 +970,60 @@ static int add_llvm_annotation(Dwarf_Die *die, int component_idx, struct conf_lo
 	if (strcmp(name, "btf_decl_tag") != 0)
 		return 0;
 
-	annot = zalloc(sizeof(*annot));
+	annot = die__create_new_llvm_annotation(die, cu, conf);
 	if (!annot)
 		return -ENOMEM;
 
+	/* Don't assign id for btf_decl_tag */
+
+	annot->kind = BTF_DECL_TAG;
 	annot->value = attr_string(die, DW_AT_const_value, conf);
 	annot->component_idx = component_idx;
 	list_add_tail(&annot->node, head);
 	return 0;
 }
 
-static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx,
+static int add_btf_type_tag(Dwarf_Die *die,
+			    struct cu *cu,
+			    struct conf_load *conf,
+			    struct list_head *head)
+{
+	struct llvm_annotation *annot;
+	const char *name;
+	bool v1, v2;
+
+	if (conf->skip_encoding_btf_type_tag)
+		return 0;
+
+	name = attr_string(die, DW_AT_name, conf);
+	v1 = strcmp(name, "btf_type_tag") == 0;
+	v2 = strcmp(name, "btf:type_tag") == 0;
+
+	if (!v1 && !v2)
+		return 0;
+
+	/* Create a btf_type_tag type for this annotation. */
+	annot = die__create_new_llvm_annotation(die, cu, conf);
+	if (annot == NULL)
+		return -ENOMEM;
+
+	annot->kind = v2 ? BTF_TYPE_TAG : BTF_TYPE_TAG_POINTEE;
+	annot->value = attr_string(die, DW_AT_const_value, conf);
+	annot->component_idx = -1;
+	if (v2)
+		cu->ignore_btf_type_tag_pointee = 1;
+
+	/* For a list of DW_TAG_LLVM_annotation like tag1 -> tag2 -> tag3,
+	 * the tag->tags contains tag3 -> tag2 -> tag1.
+	 */
+	list_add(&annot->node, head);
+
+	return 0;
+}
+
+static int add_child_llvm_annotations(Dwarf_Die *die,
+				      struct cu *cu,
+				      int component_idx,
 				      struct conf_load *conf, struct list_head *head)
 {
 	Dwarf_Die child;
@@ -912,9 +1035,14 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx,
 	die = &child;
 	do {
 		if (dwarf_tag(die) == DW_TAG_LLVM_annotation) {
-			ret = add_llvm_annotation(die, component_idx, conf, head);
+			ret = add_btf_decl_tag(die, cu, component_idx, conf, head);
+			if (ret)
+				return ret;
+			ret = add_btf_type_tag(die, cu, conf, head);
 			if (ret)
 				return ret;
+		} else {
+			cu__tag_not_handled(die);
 		}
 	} while (dwarf_siblingof(die, die) == 0);
 
@@ -1340,19 +1468,8 @@ static uint64_t attr_upper_bound(Dwarf_Die *die)
 	return 0;
 }
 
-static void __cu__tag_not_handled(Dwarf_Die *die, const char *fn)
-{
-	uint32_t tag = dwarf_tag(die);
-
-	fprintf(stderr, "%s: DW_TAG_%s (%#x) @ <%#llx> not handled!\n",
-		fn, dwarf_tag_name(tag), tag,
-		(unsigned long long)dwarf_dieoffset(die));
-}
-
 static struct tag unsupported_tag;
 
-#define cu__tag_not_handled(die) __cu__tag_not_handled(die, __FUNCTION__)
-
 static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu,
 				      int toplevel, const char *fn, struct conf_load *conf);
 
@@ -1372,87 +1489,44 @@ static struct tag *die__create_new_tag(Dwarf_Die *die, struct cu *cu)
 	return tag;
 }
 
-static struct btf_type_tag_ptr_type *die__create_new_btf_type_tag_ptr_type(Dwarf_Die *die, struct cu *cu)
+static struct tag *die__create_new_unspecified_type(Dwarf_Die *die, struct cu *cu,
+						    struct conf_load *conf)
 {
-	struct btf_type_tag_ptr_type *tag;
+	struct unspecified_type *tag;
 
-	tag  = tag__alloc_with_spec(cu, sizeof(struct btf_type_tag_ptr_type));
+	tag  = tag__alloc_with_spec(cu, sizeof(struct unspecified_type));
 	if (tag == NULL)
 		return NULL;
 
 	tag__init(&tag->tag, cu, die);
-	tag->tag.has_btf_type_tag = true;
-	INIT_LIST_HEAD(&tag->tags);
-	return tag;
-}
-
-static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *die, struct cu *cu,
-								   struct conf_load *conf)
-{
-	struct btf_type_tag_type *tag;
+	INIT_LIST_HEAD(&tag->node);
 
-	tag  = tag__alloc_with_spec(cu, sizeof(struct btf_type_tag_type));
-	if (tag == NULL)
+	tag->name = attr_string(die, DW_AT_name, conf);
+	if (add_child_llvm_annotations(die, cu, -1, conf, &tag->tag.annots))
 		return NULL;
 
-	tag__init(&tag->tag, cu, die);
-	tag->value = attr_string(die, DW_AT_const_value, conf);
-	return tag;
+	list_add(&tag->node, &cu->unspecified_types);
+
+	return &tag->tag;
 }
 
-static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu,
-					       struct conf_load *conf)
+static void unspecified_type__delete(struct cu *cu, struct unspecified_type *utype)
 {
-	struct btf_type_tag_ptr_type *tag = NULL;
-	struct btf_type_tag_type *annot;
-	Dwarf_Die *cdie, child;
-	const char *name;
-	uint32_t id;
-
-	/* If no child tags or skipping btf_type_tag encoding, just create a new tag
-	 * and return
-	 */
-	if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0 ||
-	    conf->skip_encoding_btf_type_tag)
-		return tag__new(die, cu);
+	struct dwarf_tag *dtag = utype->tag.priv;
 
-	/* Otherwise, check DW_TAG_LLVM_annotation child tags */
-	cdie = &child;
-	do {
-		if (dwarf_tag(cdie) != DW_TAG_LLVM_annotation)
-			continue;
-
-		/* Only check btf_type_tag annotations */
-		name = attr_string(cdie, DW_AT_name, conf);
-		if (strcmp(name, "btf_type_tag") != 0)
-			continue;
-
-		if (tag == NULL) {
-			/* Create a btf_type_tag_ptr type. */
-			tag = die__create_new_btf_type_tag_ptr_type(die, cu);
-			if (!tag)
-				return NULL;
-		}
-
-		/* Create a btf_type_tag type for this annotation. */
-		annot = die__create_new_btf_type_tag_type(cdie, cu, conf);
-		if (annot == NULL)
-			return NULL;
-
-		if (cu__table_add_tag(cu, &annot->tag, &id) < 0)
-			return NULL;
+	cu__free(cu, dtag);
+	cu__free(cu, utype);
+}
 
-		struct dwarf_tag *dtag = annot->tag.priv;
-		dtag->small_id = id;
-		cu__hash(cu, &annot->tag);
+static struct tag *die__create_new_annotated_tag(Dwarf_Die *die, struct cu *cu,
+						 struct conf_load *conf)
+{
+	struct tag *tag = tag__new(die, cu);
 
-		/* For a list of DW_TAG_LLVM_annotation like tag1 -> tag2 -> tag3,
-		 * the tag->tags contains tag3 -> tag2 -> tag1.
-		 */
-		list_add(&annot->node, &tag->tags);
-	} while (dwarf_siblingof(cdie, cdie) == 0);
+	if (add_child_llvm_annotations(die, cu, -1, conf, &tag->annots))
+		return NULL;
 
-	return tag ? &tag->tag : tag__new(die, cu);
+	return tag;
 }
 
 static struct tag *die__create_new_ptr_to_member_type(Dwarf_Die *die,
@@ -1527,9 +1601,8 @@ static struct tag *die__create_new_base_type(Dwarf_Die *die, struct cu *cu, stru
 	if (base == NULL)
 		return NULL;
 
-	if (dwarf_haschildren(die))
-		fprintf(stderr, "%s: DW_TAG_base_type WITH children!\n",
-			__func__);
+	if (add_child_llvm_annotations(die, cu, -1, conf, &base->tag.annots))
+		return NULL;
 
 	return &base->tag;
 }
@@ -1541,13 +1614,13 @@ static struct tag *die__create_new_typedef(Dwarf_Die *die, struct cu *cu, struct
 	if (tdef == NULL)
 		return NULL;
 
-	if (add_child_llvm_annotations(die, -1, conf, &tdef->namespace.annots))
+	if (add_child_llvm_annotations(die, cu, -1, conf, &tdef->namespace.tag.annots))
 		return NULL;
 
 	return &tdef->namespace.tag;
 }
 
-static struct tag *die__create_new_array(Dwarf_Die *die, struct cu *cu)
+static struct tag *die__create_new_array(Dwarf_Die *die, struct cu *cu, struct conf_load *conf)
 {
 	Dwarf_Die child;
 	/* "64 dimensions will be enough for everybody." acme, 2006 */
@@ -1563,17 +1636,25 @@ static struct tag *die__create_new_array(Dwarf_Die *die, struct cu *cu)
 
 	die = &child;
 	do {
-		if (dwarf_tag(die) == DW_TAG_subrange_type) {
+		switch (dwarf_tag(die)) {
+		case DW_TAG_subrange_type:
 			nr_entries[array->dimensions++] = attr_upper_bound(die);
 			if (array->dimensions == max_dimensions) {
 				fprintf(stderr, "%s: only %u dimensions are "
 						"supported!\n",
 					__FUNCTION__, max_dimensions);
-				break;
+				goto _break;
 			}
-		} else
+			break;
+		case DW_TAG_LLVM_annotation:
+			if (add_btf_type_tag(die, cu, conf, &array->tag.annots))
+				goto out_free;
+			break;
+		default:
 			cu__tag_not_handled(die);
+		}
 	} while (dwarf_siblingof(die, die) == 0);
+_break:
 
 	array->nr_entries = memdup(nr_entries,
 				   array->dimensions * sizeof(uint32_t), cu);
@@ -1610,7 +1691,8 @@ static struct tag *die__create_new_parameter(Dwarf_Die *die,
 	if (ftype != NULL) {
 		ftype__add_parameter(ftype, parm);
 		if (param_idx >= 0) {
-			if (add_child_llvm_annotations(die, param_idx, conf, &(tag__function(&ftype->tag)->annots)))
+			if (add_child_llvm_annotations(die, cu, param_idx, conf,
+						       &ftype->tag.annots))
 				return NULL;
 		}
 	} else {
@@ -1651,7 +1733,7 @@ static struct tag *die__create_new_variable(Dwarf_Die *die, struct cu *cu, struc
 {
 	struct variable *var = variable__new(die, cu, conf);
 
-	if (var == NULL || add_child_llvm_annotations(die, -1, conf, &var->annots))
+	if (var == NULL || add_child_llvm_annotations(die, cu, -1, conf, &var->annots))
 		return NULL;
 
 	return &var->ip.tag;
@@ -1684,6 +1766,10 @@ static struct tag *die__create_new_subroutine_type(Dwarf_Die *die,
 		case DW_TAG_unspecified_parameters:
 			ftype->unspec_parms = 1;
 			continue;
+		case DW_TAG_LLVM_annotation:
+			if (add_btf_type_tag(die, cu, conf, &ftype->tag.annots))
+				goto out_delete;
+			continue;
 		default:
 			tag = die__process_tag(die, cu, 0, conf);
 			if (tag == NULL)
@@ -1740,17 +1826,25 @@ static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu, st
 
 	die = &child;
 	do {
-		struct enumerator *enumerator;
+		switch (dwarf_tag(die)) {
+		case DW_TAG_enumerator: {
+			struct enumerator *enumerator;
 
-		if (dwarf_tag(die) != DW_TAG_enumerator) {
+			enumerator = enumerator__new(die, cu, conf);
+			if (enumerator == NULL)
+				goto out_delete;
+
+			enumeration__add(enumeration, enumerator);
+			break;
+		}
+		case DW_TAG_LLVM_annotation:
+			if (add_btf_type_tag(die, cu, conf,
+					     &enumeration->namespace.tag.annots))
+				goto out_delete;
+			break;
+		default:
 			cu__tag_not_handled(die);
-			continue;
 		}
-		enumerator = enumerator__new(die, cu, conf);
-		if (enumerator == NULL)
-			goto out_delete;
-
-		enumeration__add(enumeration, enumerator);
 	} while (dwarf_siblingof(die, die) == 0);
 out:
 	return &enumeration->namespace.tag;
@@ -1806,13 +1900,16 @@ static int die__process_class(Dwarf_Die *die, struct type *class,
 
 			type__add_member(class, member);
 			cu__hash(cu, &member->tag);
-			if (add_child_llvm_annotations(die, member_idx, conf, &class->namespace.annots))
+			if (add_child_llvm_annotations(die, cu, member_idx, conf,
+						       &class->namespace.tag.annots))
 				return -ENOMEM;
 			member_idx++;
 		}
 			continue;
 		case DW_TAG_LLVM_annotation:
-			if (add_llvm_annotation(die, -1, conf, &class->namespace.annots))
+			if (add_btf_decl_tag(die, cu, -1, conf, &class->namespace.tag.annots))
+				return -ENOMEM;
+			if (add_btf_type_tag(die, cu, conf, &class->namespace.tag.annots))
 				return -ENOMEM;
 			continue;
 		default: {
@@ -2089,7 +2186,8 @@ static int die__process_function(Dwarf_Die *die, struct ftype *ftype,
 				goto out_enomem;
 			continue;
 		case DW_TAG_LLVM_annotation:
-			if (add_llvm_annotation(die, -1, conf, &(tag__function(&ftype->tag)->annots)))
+			if (add_btf_decl_tag(die, cu, -1, conf,
+					     &(tag__function(&ftype->tag)->annots)))
 				goto out_enomem;
 			continue;
 		default:
@@ -2149,23 +2247,23 @@ static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu,
 	case DW_TAG_imported_unit:
 		return NULL; // We don't support imported units yet, so to avoid segfaults
 	case DW_TAG_array_type:
-		tag = die__create_new_array(die, cu);		break;
+		tag = die__create_new_array(die, cu, conf);	break;
 	case DW_TAG_string_type: // FORTRAN stuff, looks like an array
 		tag = die__create_new_string_type(die, cu);	break;
 	case DW_TAG_base_type:
 		tag = die__create_new_base_type(die, cu, conf);	break;
-	case DW_TAG_const_type:
 	case DW_TAG_imported_declaration:
 	case DW_TAG_imported_module:
 	case DW_TAG_reference_type:
-	case DW_TAG_restrict_type:
-	case DW_TAG_volatile_type:
 	case DW_TAG_atomic_type:
 		tag = die__create_new_tag(die, cu);		break;
-	case DW_TAG_unspecified_type:
-		tag = die__create_new_tag(die, cu);		break;
+	case DW_TAG_const_type:
+	case DW_TAG_restrict_type:
+	case DW_TAG_volatile_type:
 	case DW_TAG_pointer_type:
-		tag = die__create_new_pointer_tag(die, cu, conf);	break;
+		tag = die__create_new_annotated_tag(die, cu, conf); break;
+	case DW_TAG_unspecified_type:
+		tag = die__create_new_unspecified_type(die, cu, conf); break;
 	case DW_TAG_ptr_to_member_type:
 		tag = die__create_new_ptr_to_member_type(die, cu); break;
 	case DW_TAG_enumeration_type:
@@ -2252,20 +2350,100 @@ static int die__process_unit(Dwarf_Die *die, struct cu *cu, struct conf_load *co
 	return 0;
 }
 
-static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu);
+/** Add @tuple to @ctx->mappings array, extend it if necessary. */
+static int push_btf_type_tag_mapping(struct btf_type_tag_mapping *tuple,
+				     struct recode_context *ctx)
+{
+	if (ctx->nr_allocated == ctx->nr_entries) {
+		uint32_t new_nr = ctx->nr_allocated * 2;
+		void *new_array = reallocarray(ctx->mappings, new_nr,
+					       sizeof(ctx->mappings[0]));
+		if (!new_array)
+			return -ENOMEM;
+		ctx->mappings = new_array;
+		ctx->nr_allocated = new_nr;
+	}
+
+	ctx->mappings[ctx->nr_entries++] = *tuple;
+
+	return 0;
+}
+
+/** Connect `type` fields of btf:type_tag annotations attached to a
+ *  host type. Collect information about first and last tag.
+ *  `type` field are connected as below:
+ *
+ *    tag1.type -> tag2.type -> host_type
+ *
+ *  @tags      - list of llvm_annotation objects;
+ *  @host_type - small_id of the type with attached annotations.
+ */
+static void __recode_btf_type_tags(struct list_head *tags,
+				   uint32_t host_type,
+				   struct btf_type_tag_mapping *mapping)
+{
+	struct llvm_annotation *annot;
+	struct dwarf_tag *annot_dtag;
+	struct tag *prev_tag = NULL;
+	uint32_t first_tag_id = 0;
+
+	list_for_each_entry(annot, tags, node) {
+		if (annot->kind != BTF_TYPE_TAG)
+			continue;
+		annot_dtag = annot->tag.priv;
+		if (prev_tag)
+			prev_tag->type = annot_dtag->small_id;
+		if (!first_tag_id)
+			first_tag_id = annot_dtag->small_id;
+		prev_tag = &annot->tag;
+	}
 
-static int namespace__recode_dwarf_types(struct tag *tag, struct cu *cu)
+	mapping->host_type_id = host_type;
+	if (prev_tag) {
+		prev_tag->type = host_type;
+		mapping->first_tag_id = first_tag_id;
+		mapping->last_tag_id = annot_dtag->small_id;
+	} else {
+		mapping->first_tag_id = 0;
+		mapping->last_tag_id = 0;
+	}
+}
+
+static int recode_btf_type_tags(struct list_head *tags,
+				uint32_t host_type,
+				struct recode_context *ctx)
+{
+	struct btf_type_tag_mapping mapping;
+
+	if (list_empty(tags))
+		return 0;
+
+	__recode_btf_type_tags(tags, host_type, &mapping);
+	if (!mapping.first_tag_id)
+		return 0;
+
+	return push_btf_type_tag_mapping(&mapping, ctx);
+}
+
+static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu,
+				      struct recode_context *ctx);
+
+static int namespace__recode_dwarf_types(struct tag *tag, struct cu *cu,
+					 struct recode_context *ctx)
 {
 	struct tag *pos;
 	struct dwarf_cu *dcu = cu->priv;
+	struct dwarf_tag *dtag = tag->priv;
 	struct namespace *ns = tag__namespace(tag);
 
+	recode_btf_type_tags(&tag->annots, dtag->small_id, ctx);
+
 	namespace__for_each_tag(ns, pos) {
 		struct dwarf_tag *dtype;
 		struct dwarf_tag *dpos = pos->priv;
 
 		if (tag__has_namespace(pos)) {
-			if (namespace__recode_dwarf_types(pos, cu))
+			if (namespace__recode_dwarf_types(pos, cu, ctx))
 				return -1;
 			continue;
 		}
@@ -2286,7 +2464,7 @@ static int namespace__recode_dwarf_types(struct tag *tag, struct cu *cu)
 			break;
 		case DW_TAG_subroutine_type:
 		case DW_TAG_subprogram:
-			ftype__recode_dwarf_types(pos, cu);
+			ftype__recode_dwarf_types(pos, cu, ctx);
 			break;
 		case DW_TAG_imported_module:
 			dtype = dwarf_cu__find_tag_by_ref(dcu, &dpos->type);
@@ -2351,7 +2529,8 @@ static void __tag__print_abstract_origin_not_found(struct tag *tag,
 #define tag__print_abstract_origin_not_found(tag ) \
 	__tag__print_abstract_origin_not_found(tag, __func__)
 
-static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu)
+static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu,
+				      struct recode_context *ctx)
 {
 	struct parameter *pos;
 	struct dwarf_cu *dcu = cu->priv;
@@ -2399,9 +2578,13 @@ static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu)
 		}
 		pos->tag.type = dtype->small_id;
 	}
+
+	struct dwarf_tag *dtag = tag->priv;
+	recode_btf_type_tags(&tag->annots, dtag->small_id, ctx);
 }
 
-static void lexblock__recode_dwarf_types(struct lexblock *tag, struct cu *cu)
+static void lexblock__recode_dwarf_types(struct lexblock *tag, struct cu *cu,
+					 struct recode_context *ctx)
 {
 	struct tag *pos;
 	struct dwarf_cu *dcu = cu->priv;
@@ -2412,7 +2595,7 @@ static void lexblock__recode_dwarf_types(struct lexblock *tag, struct cu *cu)
 
 		switch (pos->tag) {
 		case DW_TAG_lexical_block:
-			lexblock__recode_dwarf_types(tag__lexblock(pos), cu);
+			lexblock__recode_dwarf_types(tag__lexblock(pos), cu, ctx);
 			continue;
 		case DW_TAG_inlined_subroutine:
 			if (dpos->type.off != 0)
@@ -2426,7 +2609,7 @@ static void lexblock__recode_dwarf_types(struct lexblock *tag, struct cu *cu)
 					tag__print_abstract_origin_not_found(pos);
 				continue;
 			}
-			ftype__recode_dwarf_types(dtype->tag, cu);
+			ftype__recode_dwarf_types(dtype->tag, cu, ctx);
 			continue;
 
 		case DW_TAG_formal_parameter:
@@ -2493,10 +2676,146 @@ static void lexblock__recode_dwarf_types(struct lexblock *tag, struct cu *cu)
 	}
 }
 
-static void dwarf_cu__recode_btf_type_tag_ptr(struct btf_type_tag_ptr_type *tag,
-					      uint32_t pointee_type)
+static int recode_context_init(struct recode_context *ctx)
+{
+	const int initial_nr = 16;
+
+	ctx->mappings = reallocarray(NULL, initial_nr, sizeof(ctx->mappings[0]));
+	if (!ctx->mappings)
+		return -ENOMEM;
+
+	ctx->nr_allocated = initial_nr;
+	ctx->nr_entries = 0;
+
+	return 0;
+}
+
+static void recode_context_free(struct recode_context *ctx)
+{
+	free(ctx->mappings);
+	ctx->nr_allocated = 0;
+	ctx->nr_entries = 0;
+}
+
+/** Compare two `btf_type_tag_mapping` objects using host_type_id as key. */
+static int compare_btf_type_tag_recode_mappings(const void *_a, const void *_b)
+{
+	const struct btf_type_tag_mapping *a = _a;
+	const struct btf_type_tag_mapping *b = _b;
+	long diff = (long)a->host_type_id - (long)b->host_type_id;
+
+	if (diff < 0)
+		return -1;
+	if (diff > 0)
+		return 1;
+	return 0;
+}
+
+/** Sort @ctx->mappings array by btf_type_tag_mapping::host_type_id,
+ *  function `lookup_btf_type_tag_by_host()` uses binary search to find
+ *  elements of this array.
+ */
+static void sort_btf_type_tags(struct recode_context *ctx)
+{
+	qsort(ctx->mappings, ctx->nr_entries, sizeof(ctx->mappings[0]),
+	      compare_btf_type_tag_recode_mappings);
+}
+
+static struct btf_type_tag_mapping *lookup_btf_type_tag_by_host(uint32_t host_type_id,
+								struct recode_context *ctx)
+{
+	struct btf_type_tag_mapping key = { .host_type_id = host_type_id };
+
+	return bsearch(&key, ctx->mappings, ctx->nr_entries, sizeof(key),
+		       compare_btf_type_tag_recode_mappings);
+}
+
+/** Update @tag->type fields by replacing types by ids of associated
+ *  btf:type_tag objects. @ctx->mappings should be sorted when this
+ *  function is called.
+ *
+ *  When "btf:type_tag" is attached to a type it applies to this type.
+ *  For example, the following dwarf:
+ *
+ *  0x00000040:     DW_TAG_member
+ *                    DW_AT_name    ("c")
+ *                    DW_AT_type    (0x0000008c "int")
+ *                    DW_AT_decl_file       ("/home/eddy/work/tmp/test.c")
+ *                    DW_AT_decl_line       (13)
+ *                    DW_AT_data_member_location    (0x10)
+ *
+ *  0x0000008c:   DW_TAG_base_type
+ *                  DW_AT_name      ("int")
+ *                  DW_AT_encoding  (DW_ATE_signed)
+ *                  DW_AT_byte_size (0x04)
+ *
+ *  0x00000090:     DW_TAG_LLVM_annotation
+ *                    DW_AT_name    ("btf:type_tag")
+ *                    DW_AT_const_value     ("__c")
+ *
+ *  Means that type 0x0000008c is an `int` with type tag `__c`.
+ *  Corresponding BTF looks as follows:
+ *
+ *  [1] STRUCT 'echo'
+ *          ...
+ *          'c' type_id=8 bits_offset=128
+ *  [4] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
+ *  [8] TYPE_TAG '__c' type_id=4
+ *
+ *  Before the call to `update_btf_type_tag_refs` for member 0x00000040
+ *  its `type` field points to 0x0000008c. `update_btf_type_tag_refs`
+ *  updates this link to point to `0x00000090` instead, thus obtaining the
+ *  desired BTF shape.
+ */
+static int update_btf_type_tag_refs(struct tag *tag,
+				    struct cu *cu,
+				    struct recode_context *ctx)
+{
+	struct btf_type_tag_mapping *tuple;
+	struct dwarf_tag *dtag = tag->priv;
+
+	/* Kernel does not support VAR entries with types of form
+	 * 'VAR -> TYPE_TAG -> something':
+	 * - in verifier.c:check_pseudo_btf_id() instruction auxiliary
+	 *   data is set to point to variable type w/o stripping modifiers;
+	 * - btf.c:btf_struct_access() -> btf.c:btf_struct_walk()
+	 *   does not skip modifiers prior to btf_type_is_struct() check.
+	 *
+	 * So, skip type tag recoding for variables.
+	 */
+	if (tag->tag == DW_TAG_variable)
+		return 0;
+
+	tuple = lookup_btf_type_tag_by_host(tag->type, ctx);
+	/* Avoid creation of circular references, last btf:type_tag
+	 * object points to the host type.
+	 */
+	if (tuple && tuple->last_tag_id != dtag->small_id)
+		tag->type = tuple->first_tag_id;
+
+	if (tag__is_type(tag)) {
+		struct type *type = tag__type(tag);
+		struct class_member *pos;
+
+		type__for_each_data_member(type, pos)
+			update_btf_type_tag_refs(&pos->tag, cu, ctx);
+	} else if (tag->tag == DW_TAG_subprogram ||
+		   tag->tag == DW_TAG_subroutine_type) {
+		struct ftype *ftype = tag__ftype(tag);
+		struct parameter *pos;
+
+		ftype__for_each_parameter(ftype, pos)
+			update_btf_type_tag_refs(&pos->tag, cu, ctx);
+	}
+
+	return 0;
+}
+
+static void dwarf_cu__recode_btf_type_tag_ptr(struct tag *tag,
+					      uint32_t pointee_type,
+					      struct cu *cu)
 {
-	struct btf_type_tag_type *annot;
+	struct llvm_annotation *annot;
 	struct dwarf_tag *annot_dtag;
 	struct tag *prev_tag;
 
@@ -2523,16 +2842,20 @@ static void dwarf_cu__recode_btf_type_tag_ptr(struct btf_type_tag_ptr_type *tag,
 	 *   int tag1 tag2 tag3 *p;
 	 * and this matches the user/kernel code.
 	 */
-	prev_tag = &tag->tag;
-	list_for_each_entry(annot, &tag->tags, node) {
-		annot_dtag = annot->tag.priv;
-		prev_tag->type = annot_dtag->small_id;
-		prev_tag = &annot->tag;
+	prev_tag = tag;
+	if (!cu->ignore_btf_type_tag_pointee) {
+		list_for_each_entry(annot, &tag->annots, node) {
+			if (annot->kind != BTF_TYPE_TAG_POINTEE)
+				continue;
+			annot_dtag = annot->tag.priv;
+			prev_tag->type = annot_dtag->small_id;
+			prev_tag = &annot->tag;
+		}
 	}
 	prev_tag->type = pointee_type;
 }
 
-static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu)
+static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu, struct recode_context *ctx)
 {
 	struct dwarf_tag *dtag = tag->priv;
 	struct dwarf_tag *dtype;
@@ -2545,7 +2868,7 @@ static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu)
 		type__recode_dwarf_specification(tag, cu);
 
 	if (tag__has_namespace(tag))
-		return namespace__recode_dwarf_types(tag, cu);
+		return namespace__recode_dwarf_types(tag, cu, ctx);
 
 	switch (tag->tag) {
 	case DW_TAG_subprogram: {
@@ -2577,17 +2900,17 @@ static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu)
 					(unsigned long long)specification.off);
 			}
 		}
-		lexblock__recode_dwarf_types(&fn->lexblock, cu);
+		lexblock__recode_dwarf_types(&fn->lexblock, cu, ctx);
 	}
 		/* Fall thru */
 
 	case DW_TAG_subroutine_type:
-		ftype__recode_dwarf_types(tag, cu);
+		ftype__recode_dwarf_types(tag, cu, ctx);
 		/* Fall thru, for the function return type */
 		break;
 
 	case DW_TAG_lexical_block:
-		lexblock__recode_dwarf_types(tag__lexblock(tag), cu);
+		lexblock__recode_dwarf_types(tag__lexblock(tag), cu, ctx);
 		return 0;
 
 	case DW_TAG_ptr_to_member_type: {
@@ -2608,7 +2931,7 @@ static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu)
 		break;
 
 	case DW_TAG_namespace:
-		return namespace__recode_dwarf_types(tag, cu);
+		return namespace__recode_dwarf_types(tag, cu, ctx);
 	/* Damn, DW_TAG_inlined_subroutine is an special case
            as dwarf_tag->id is in fact an abtract origin, i.e. must be
 	   looked up in the tags_table, not in the types_table.
@@ -2636,15 +2959,19 @@ static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu)
 					var->spec = tag__variable(dtype->tag);
 			}
 		}
+		break;
 	}
-
+	case DW_TAG_LLVM_annotation:
+		return 0;
 	}
 
+	recode_btf_type_tags(&tag->annots, dtag->small_id, ctx);
+
 	if (dtag->type.off == 0) {
-		if (tag->tag != DW_TAG_pointer_type || !tag->has_btf_type_tag)
+		if (tag->tag != DW_TAG_pointer_type)
 			tag->type = 0; /* void */
 		else
-			dwarf_cu__recode_btf_type_tag_ptr(tag__btf_type_tag_ptr(tag), 0);
+			dwarf_cu__recode_btf_type_tag_ptr(tag, 0, cu);
 		return 0;
 	}
 
@@ -2656,10 +2983,10 @@ check_type:
 		return 0;
 	}
 out:
-	if (tag->tag != DW_TAG_pointer_type || !tag->has_btf_type_tag)
+	if (tag->tag != DW_TAG_pointer_type)
 		tag->type = dtype->small_id;
 	else
-		dwarf_cu__recode_btf_type_tag_ptr(tag__btf_type_tag_ptr(tag), dtype->small_id);
+		dwarf_cu__recode_btf_type_tag_ptr(tag, dtype->small_id, cu);
 
 	return 0;
 }
@@ -2744,28 +3071,168 @@ static int cu__resolve_func_ret_types_optimized(struct cu *cu)
 	return 0;
 }
 
-static int cu__recode_dwarf_types_table(struct cu *cu,
-					struct ptr_table *pt,
-					uint32_t i)
+typedef int (*recode_tag_visitor)(struct tag *, struct cu*, struct recode_context *);
+
+static int cu__visit_all_tags(struct cu *cu, recode_tag_visitor fn, struct recode_context *ctx)
 {
-	for (; i < pt->nr_entries; ++i) {
-		struct tag *tag = pt->entries[i];
+	const int nr_tables = 3;
+	struct {
+		struct ptr_table *table;
+		uint32_t start_idx;
+	} tables[] = {
+		{ &cu->types_table,	1 },
+		{ &cu->tags_table,	0 },
+		{ &cu->functions_table,	0 }
+	};
+	struct ptr_table *pt;
+	struct tag *tag;
+	uint32_t i, t;
 
-		if (tag != NULL) /* void, see cu__new */
-			if (tag__recode_dwarf_type(tag, cu))
+	for (t = 0; t < nr_tables; ++t) {
+		pt = tables[t].table;
+		for (i = tables[t].start_idx; i < pt->nr_entries; ++i) {
+			tag = pt->entries[i];
+			if (!tag) /* void, see cu__new */
+				continue;
+			if (fn(tag, cu, ctx))
 				return -1;
+		}
+	}
+
+	return 0;
+}
+
+/* See comment for cu__allocate_btf_type_tags() below. */
+static int tag__allocate_btf_type_tags(struct tag *tag, struct cu *cu)
+{
+	enum annotation_kind target = cu->ignore_btf_type_tag_pointee
+				      ? BTF_TYPE_TAG
+				      : BTF_TYPE_TAG_POINTEE;
+	struct list_head *annots = &tag->annots;
+	struct llvm_annotation *annot;
+
+	list_for_each_entry(annot, annots, node) {
+		if (annot->kind != target)
+			continue;
+
+		int err = cu__assign_tag_id(cu, &annot->tag);
+		if (err)
+			return err;
 	}
 
 	return 0;
 }
 
+/* The flag `cu->ignore_btf_type_tag_pointee` is set at DWARF load phase.
+ * Before it is set it is not known what kind of type tags is used in
+ * the program, BTF_TYPE_TAG or BTF_TYPE_TAG_POINTEE.
+ * It is necessary to allocate only a single kind of tags to avoid
+ * spurious entries in the type table (and resultant BTF).
+ * Thus, the allocation of type tags is done as a separate step.
+ */
+static int cu__allocate_btf_type_tags(struct cu *cu)
+{
+	struct unspecified_type *utype;
+	struct tag *pos;
+	uint32_t id;
+	int err = 0;
+
+	cu__for_each_type(cu, id, pos) {
+		err = tag__allocate_btf_type_tags(pos, cu);
+		if (err)
+			return err;
+	}
+
+	list_for_each_entry(utype, &cu->unspecified_types, node) {
+		err = tag__allocate_btf_type_tags(&utype->tag, cu);
+		if (err)
+			return err;
+	}
+
+	return err;
+}
+
+/* `btf:type_tag` tags have special representation, when attached to void type.
+ * For example, DWARF for the following C code:
+ *
+ *   struct st {
+ *     void __attribute__(btf_type_tag("__d")) *d;
+ *   }
+ *
+ * Looks as follows:
+ *
+ *   0x29:   DW_TAG_structure_type
+ *             DW_AT_name      ("st")
+ *
+ *   0x49:     DW_TAG_member
+ *               DW_AT_name    ("d")
+ *               DW_AT_type    (0xa6 "void *")
+ *
+ *   0xa6:   DW_TAG_pointer_type
+ *             DW_AT_type      (0xaf "void")
+ *
+ *   0xaf:   DW_TAG_unspecified_type
+ *             DW_AT_name      ("void")
+ *
+ *   0xb1:     DW_TAG_LLVM_annotation
+ *               DW_AT_name    ("btf:type_tag")
+ *               DW_AT_const_value     ("__d")
+ *
+ * Here `void` type is encoded as `DW_TAG_unspecified_type` with
+ * `DW_TAG_LLVM_annotation` children.
+ *
+ * This function replaces `small_id` of the unspecified type (0xaf) with
+ * `small_id` of the first `btf:type_tag` annotation (0xb1).
+ * Thus further recode passes will use id of the type tag in place of the
+ * unspecified type id, when references to unspecified type are resolved.
+ */
+static void cu__recode_unspecified_types(struct cu *cu)
+{
+	struct btf_type_tag_mapping mapping;
+	struct unspecified_type *utype;
+	struct dwarf_tag *dtag;
+
+	list_for_each_entry(utype, &cu->unspecified_types, node) {
+		__recode_btf_type_tags(&utype->tag.annots, 0, &mapping);
+		dtag = utype->tag.priv;
+		dtag->small_id = mapping.first_tag_id;
+	}
+}
+
 static int cu__recode_dwarf_types(struct cu *cu)
 {
-	if (cu__recode_dwarf_types_table(cu, &cu->types_table, 1) ||
-	    cu__recode_dwarf_types_table(cu, &cu->tags_table, 0) ||
-	    cu__recode_dwarf_types_table(cu, &cu->functions_table, 0))
+	struct recode_context ctx = {};
+	int err = 0;
+
+	if (recode_context_init(&ctx))
 		return -1;
-	return 0;
+
+	if (cu__allocate_btf_type_tags(cu)) {
+		err = -1;
+		goto cleanup;
+	}
+
+	cu__recode_unspecified_types(cu);
+
+	if (cu__visit_all_tags(cu, tag__recode_dwarf_type, &ctx)) {
+		err = -1;
+		goto cleanup;
+	}
+
+	/* No need for second pass if there are no btf type tags */
+	if (ctx.nr_entries == 0)
+		goto cleanup;
+
+	sort_btf_type_tags(&ctx);
+
+	if (cu__visit_all_tags(cu, update_btf_type_tag_refs, &ctx)) {
+		err = -1;
+		goto cleanup;
+	}
+
+cleanup:
+	recode_context_free(&ctx);
+	return err;
 }
 
 static const char *dwarf_tag__decl_file(const struct tag *tag,
diff --git a/dwarves.c b/dwarves.c
index b43031c..7e66a98 100644
--- a/dwarves.c
+++ b/dwarves.c
@@ -681,6 +681,7 @@ struct cu *cu__new(const char *name, uint8_t addr_size,
 		cu->dfops	= NULL;
 		INIT_LIST_HEAD(&cu->tags);
 		INIT_LIST_HEAD(&cu->tool_list);
+		INIT_LIST_HEAD(&cu->unspecified_types);
 
 		cu->addr_size = addr_size;
 		cu->extra_dbg_info = 0;
diff --git a/dwarves.h b/dwarves.h
index e92b2fd..8547fea 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -240,6 +240,7 @@ struct cu {
 	struct list_head node;
 	struct list_head tags;
 	struct list_head tool_list;	/* To be used by tools such as ctracer */
+	struct list_head unspecified_types;
 	struct ptr_table types_table;
 	struct ptr_table functions_table;
 	struct ptr_table tags_table;
@@ -258,6 +259,10 @@ struct cu {
 	uint8_t		 has_addr_info:1;
 	uint8_t		 uses_global_strings:1;
 	uint8_t		 little_endian:1;
+	/* When set means that "btf:type_tags" annotations are present
+	 * and "btf_type_tags" annotations should be ignored during export.
+	 */
+	uint8_t		 ignore_btf_type_tag_pointee:1;
 	uint8_t		 nr_register_params;
 	int		 register_params[ARCH_MAX_REGISTER_PARAMS];
 	uint16_t	 language;
@@ -417,8 +422,8 @@ int cu__for_all_tags(struct cu *cu,
 		     void *cookie);
 
 /** struct tag - basic representation of a debug info element
- * @priv - extra data, for instance, DWARF offset, id, decl_{file,line}
- * @top_level -
+ * @priv   - extra data, for instance, DWARF offset, id, decl_{file,line}
+ * @annots - list of btf_type_tag and btf_decl_tag annotations.
  */
 struct tag {
 	struct list_head node;
@@ -426,8 +431,8 @@ struct tag {
 	uint16_t	 tag;
 	bool		 visited;
 	bool		 top_level;
-	bool		 has_btf_type_tag;
 	uint16_t	 recursivity_level;
+	struct list_head annots;
 	void		 *priv;
 };
 
@@ -623,43 +628,51 @@ static inline struct ptr_to_member_type *
 	return (struct ptr_to_member_type *)tag;
 }
 
-struct llvm_annotation {
-	const char		*value;
-	int16_t			component_idx;
-	struct list_head	node;
+enum annotation_kind {
+	BTF_DECL_TAG,
+	/* "btf_type_tag" in DWARF, attached to a pointer, applies to pointee type.
+	 * Old-style encoding kept for backwards compatibility.
+	 */
+	BTF_TYPE_TAG_POINTEE,
+	/* "btf:type_tag" in DWARF, attached to any type, applies to parent type */
+	BTF_TYPE_TAG,
 };
 
-/** struct btf_type_tag_type - representing a btf_type_tag annotation
+/** struct llvm_annotation - representing objects with DW_TAG_LLVM_annotation tag
  *
- * @tag   - DW_TAG_LLVM_annotation tag
- * @value - btf_type_tag value string
- * @node  - list_head node
+ * @tag           - DW_TAG_LLVM_annotation tag
+ * @kind          - annotation kind
+ * @value         - value string, valid for both "btf_decl_tag" and "btf_type_tag"
+ * @component_idx - component index, valid only for "btf_decl_tag"
+ * @node          - list_head node
  */
-struct btf_type_tag_type {
+struct llvm_annotation {
 	struct tag		tag;
+	enum annotation_kind	kind;
 	const char		*value;
+	int16_t			component_idx;
 	struct list_head	node;
 };
 
-/** The struct btf_type_tag_ptr_type - type containing both pointer type and
- *  its btf_type_tag annotations
+static inline struct llvm_annotation *tag__llvm_annotation(struct tag *tag)
+{
+	return (struct llvm_annotation *)tag;
+}
+
+/** struct unspecified_type - representation of DW_TAG_unspecified_type.
  *
- * @tag  - pointer type tag
- * @tags - btf_type_tag annotations for the pointer type
+ *  @name   - DW_AT_name associated with this tag
+ *  @node   - a node for cu::unspecified_types list
  */
-struct btf_type_tag_ptr_type {
+struct unspecified_type {
 	struct tag		tag;
-	struct list_head 	tags;
+	const char		*name;
+	struct list_head	node;
 };
 
-static inline struct btf_type_tag_ptr_type *tag__btf_type_tag_ptr(struct tag *tag)
-{
-	return (struct btf_type_tag_ptr_type *)tag;
-}
-
-static inline struct btf_type_tag_type *tag__btf_type_tag(struct tag *tag)
+static inline struct unspecified_type *tag__unspecified_type(struct tag *tag)
 {
-	return (struct btf_type_tag_type *)tag;
+	return (struct unspecified_type *)tag;
 }
 
 /** struct namespace - base class for enums, structs, unions, typedefs, etc
@@ -673,7 +686,6 @@ struct namespace {
 	uint16_t	 nr_tags;
 	uint8_t		 shared_tags;
 	struct list_head tags;
-	struct list_head annots;
 };
 
 static inline struct namespace *tag__namespace(const struct tag *tag)
@@ -1339,6 +1351,10 @@ enum base_type_float_type {
 	BT_FP_IMGRY_LDBL
 };
 
+/** struct base_type - represents types with DW_TAG_base_type
+ *
+ * @tags  - an optional list of btf_type_tag annotations
+ */
 struct base_type {
 	struct tag	tag;
 	const char	*name;
diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c
index 30355b4..baa5924 100644
--- a/dwarves_fprintf.c
+++ b/dwarves_fprintf.c
@@ -571,6 +571,7 @@ static const char *__tag__name(const struct tag *tag, const struct cu *cu,
 	case DW_TAG_restrict_type:
 	case DW_TAG_atomic_type:
 	case DW_TAG_unspecified_type:
+	case DW_TAG_LLVM_annotation:
 		type = cu__type(cu, tag->type);
 		if (type == NULL && tag->type != 0)
 			tag__id_not_found_snprintf(bf, len, tag->type);
@@ -782,6 +783,10 @@ next_type:
 			n = tag__has_type_loop(type, ptype, NULL, 0, fp);
 			if (n)
 				return printed + n;
+			if (ptype->tag == DW_TAG_LLVM_annotation) {
+				type = ptype;
+				goto next_type;
+			}
 			if (ptype->tag == DW_TAG_subroutine_type) {
 				printed += ftype__fprintf(tag__ftype(ptype),
 							  cu, name, 0, 1,
@@ -874,6 +879,14 @@ print_modifier: {
 		else
 			printed += enumeration__fprintf(type, &tconf, fp);
 		break;
+	case DW_TAG_LLVM_annotation: {
+		struct tag *ttype = cu__type(cu, type->type);
+		if (ttype) {
+			type = ttype;
+			goto next_type;
+		}
+		goto out_type_not_found;
+	}
 	}
 out:
 	if (type_expanded)
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH dwarves 0/1] Support for new btf_type_tag encoding
  2023-03-13  2:17 [PATCH dwarves 0/1] Support for new btf_type_tag encoding Eduard Zingerman
  2023-03-13  2:17 ` [PATCH dwarves 1/1] dwarf_loader: Support for btf:type_tag Eduard Zingerman
@ 2023-03-13 20:32 ` Arnaldo Carvalho de Melo
  2023-03-13 23:35   ` Eduard Zingerman
  1 sibling, 1 reply; 6+ messages in thread
From: Arnaldo Carvalho de Melo @ 2023-03-13 20:32 UTC (permalink / raw)
  To: Eduard Zingerman
  Cc: dwarves, arnaldo.melo, bpf, kernel-team, ast, daniel, andrii,
	yhs, jose.marchesi, david.faust

Em Mon, Mar 13, 2023 at 04:17:43AM +0200, Eduard Zingerman escreveu:
> In recent discussion in BPF mailing list ([1]) participants agreed to
> add a new DWARF representation for "btf_type_tag" annotations.
> 
> Existing representation is DW_TAG_LLVM_annotation object attached as a
> child to a DW_TAG_pointer_type. It means that "btf_type_tag"
> annotation is attached to a pointee type.
> 
> New representation is DW_TAG_LLVM_annotation object attached as a
> child to *any* type. It means that "btf_type_tag" annotation is
> attached to the parent type.
> 
> Here is an example:
> 
>     int __attribute__((btf_type_tag("tag1"))) *g;
> 
> And corresponding DWARF:
> 
>     0x0000001e:   DW_TAG_variable
>                     DW_AT_name      ("g")
>                     DW_AT_type      (0x00000029 "int *")
> 
>     0x00000029:   DW_TAG_pointer_type
>                     DW_AT_type      (0x00000032 "int")
> 
>     0x0000002e:     DW_TAG_LLVM_annotation
>                       DW_AT_name    ("btf_type_tag")
>                       DW_AT_const_value     ("tag1")
> 
>     0x00000032:   DW_TAG_base_type
>                     DW_AT_name      ("int")
> 
> 
> This patch adds logic necessary to handle such annotations in the
> pahole tool. Examples like below are supported:
> 
>   #define __tag(val) __attribute__((btf_type_tag("__" #val)))
> 
>   struct      alpha {};
>   union       bravo {};
>   enum        charlie { X };
>   typedef int delta;
> 
>   struct echo {
>     int          * __tag(a)  a;
>     int            __tag(b) *b;
>     int            __tag(c)  c;
>     void           __tag(d) *d;
>     void           __tag(e) *e;
>     struct alpha   __tag(f)  f;
>     union  bravo   __tag(g)  g;
>     enum   charlie __tag(h)  h;
>     delta          __tag(i)  i;
>     int __tag(j_result) (__tag(j) *j)(int __tag(j_param));
>   } g;
> 
> Implementation details
> ----------------------
> 
> Although this was not discussed in the mailing list, the proposed
> implementation acts in a following way (for compatibility reasons):
> - both forms could be present in the debug info;
> - if any annotations corresponding to the new form are present in the
>   debug info, annotations corresponding to the old form are ignored.

agreed
 
> Because the new form of type tags could be applied to any type it is
> somewhat invasive from implementation point of view:

Can you please see if you can break this patch into a series so that it
helps with reviewing and bisection?

> - Types `struct btf_type_tag_type` and `struct llvm_annotation` are
>   consolidated as a single type `struct llvm_annotation`, in order to
>   reside in a single `annots` list associated with struct/union/enum
>   or typedef.
> - The `annots` list, used to hold references to annotation objects,
>   is put directly in `struct tag`.
> - At the load phase it is not yet known whether new or old form is
>   used for type tags encoding, so `struct llvm_annotation` objects are
>   not added to the types table.
> - The recode phase now consists of several steps:
>   - depending on the results of the load phase, `struct llvm_annotation`
>     objects corresponding to either old or new representations are
>     added to the types table;
>   - `tag__recode_dwarf_type()` is executed as usual, but it also
>     collects information about type tags;
>   - references to types that have type tag annotations are updated to
>     point to the first annotation object.
> 
> Corresponding clang changes are tracked in [6].
> 
> Testing
> -------
> 
> To verify the changes I used the following:
> - Tools:
>   - "LLVM-main"   :: LLVM at revision [3];
>   - "LLVM-new"    :: LLVM at revision [3] with patches [6] applied;
>   - "gcc"         :: GCC version 11.3 (no support for btf_type_tag annotations);
>   - "pahole-next" :: dwarves at revision [4];
>   - "pahole-new"  :: dwarves at revision [4] + this patch,
>   - "kernel"      :: Linux Kernel bpf-next branch at revision [5]
> - test cases:
>   - kernel build;
>   - kernel BPF test cases build, BPF tests execution
>     (test_verifier, test_progs, test_progs-no_alu32, test_maps);
>   - btfdiff script (suggested by Arnaldo, [2]).
> - tool combinations (kernel compiler / clang for BPF tests / pahole version):
>   - LLVM-main / LLVM-main / pahole-new
>     - kernel build : ok
>     - bpf tests    : ok
>     - btfdiff      : ok (modulo diff #1, see below)
>   - gcc       / LLVM-main / pahole-new
>     - kernel build : ok
>     - bpf tests    : ok
>     - btfdiff      : ok
>   - LLVM-new  / LLVM-new  / pahole-next
>     - kernel build : ok with warnings (see warn #1 below)
>     - bpf tests    : ok
>     - btfdiff      : fails (see diff #2 below)
>   - LLVM-new  / LLVM-new  / pahole-new
>     - kernel build : ok
>     - bpf tests    : ok
>     - btfdiff      : ok (modulo diff #1, see below)
>   - gcc       / LLVM-new  / pahole-new
>     - kernel build : ok
>     - bpf tests    : ok
>     - btfdiff      : ok
> 
> Note on BPF tests: test case `verif_scale_loop6` fails for all
> configurations above and 'LLVM-main / LLVM-main / pahole-next',
> thus I consider this issue as unrelated.
> 
> Diff #1: Difference in flexible printing, several occurrences as below:
> 
>   @ -10531,7 +10531,7 @ struct bpf_cand_cache {
>           struct {
>                   const struct btf  * btf;                 /*    16     8 */
>                   u32                id;                   /*    24     4 */
>   -       } cands[0]; /*    16     0 */
>   +       } cands[]; /*    16     0 */
>    
>           /* size: 16, cachelines: 1, members: 5 */
>           /* last cacheline: 16 bytes */
> 
> Diff #2: pahole-next does not know how to print type name for types
> with type tags, when reading from BTF:
> 
>   @ -72998,8 +73022,8 @ struct sock {
>           /* --- cacheline 19 boundary (1216 bytes) --- */
>           int                        (*sk_backlog_rcv)(struct sock *, struct sk_buff *); /*  1216     8 */
>           void                       (*sk_destruct)(struct sock *); /*  1224     8 */
>   -       rcu *                      sk_reuseport_cb;      /*  1232     8 */
>   -       rcu *                      sk_bpf_storage;       /*  1240     8 */
>   +       <ERROR                    > sk_reuseport_cb;     /*  1232     8 */
>   +       <ERROR                    > sk_bpf_storage;      /*  1240     8 */
> 
> (Also DWARF names refer to TYPE_TAG, not actual type name, fixed in pahole-new).
> 
> Warn #1: pahole-next complains about unexpected child tags generated
> by clang, e.g.:
> 
>   die__create_new_tag: unspecified_type WITH children!
>   die__create_new_base_type: DW_TAG_base_type WITH children!

Sure, we can remove those if the children is the expected one, leaving
the warning maybe for debug sessions.

Thanks for the detailed implementation notes, references and tests
performed, please consider breaking it up into smaller pieces or
ellaborate on why you think can't be done.

Thanks!

- Arnaldo
 
> 
> Performance impact
> ------------------
> 
> The update to `struct tag` might raise concerns regarding memory
> usage, additional steps in recode phase might raise concerns regarding
> execution time. Below is statistics collected for Kernel BTF
> generation.
> 
> LLVM-new / LLVM-new / pahole-new:
> 
> $ /usr/bin/time -v pahole -J --btf_gen_floats -j --lang_exclude=rust .tmp_vmlinux.btf
>     ...
> 	User time (seconds): 22.29
> 	System time (seconds): 0.47
> 	Percent of CPU this job got: 483%
>     ...
> 	Maximum resident set size (kbytes): 714524
>     ...
> 
> LLVM-new / LLVM-new / pahole-next:
> 
> $ /usr/bin/time -v pahole -J --btf_gen_floats -j --lang_exclude=rust .tmp_vmlinux.btf
>     ...
> 	User time (seconds): 20.96
> 	System time (seconds): 0.44
> 	Percent of CPU this job got: 473%
>     ...
> 	Maximum resident set size (kbytes): 700848
>     ...
> 
> Links & revisions
> -----------------
> 
> [1] Mailing list discussion regarding `btf:type_tag`
>     https://lore.kernel.org/bpf/87r0w9jjoq.fsf@oracle.com/
> [2] Suggestion to use btfdiff
>     https://lore.kernel.org/dwarves/ZAKpZGSHTvsS4r8E@kernel.org/T/#mddbfe661e339485fb2b0e706b31329b46bf61bda
> [3] f759275c1c8e ("[AMDGPU] Regenerate sdwa-peephole.ll")
> [4] a9498899109d ("dwarf_loader: Support for btf:type_tag")
> [5] 49b5300f1f8f ("Merge branch 'Support stashing local kptrs with bpf_kptr_xchg'")
> [6] LLVM changes to generate btf:type_tag, revisions stack:
>     https://reviews.llvm.org/D143966
>     https://reviews.llvm.org/D143967
>     https://reviews.llvm.org/D145891
> 
> Eduard Zingerman (1):
>   dwarf_loader: Support for btf:type_tag
> 
>  btf_encoder.c     |  13 +-
>  btf_loader.c      |  15 +-
>  dwarf_loader.c    | 763 +++++++++++++++++++++++++++++++++++++---------
>  dwarves.c         |   1 +
>  dwarves.h         |  68 +++--
>  dwarves_fprintf.c |  13 +
>  6 files changed, 693 insertions(+), 180 deletions(-)
> 
> -- 
> 2.39.1
> 

-- 

- Arnaldo

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH dwarves 0/1] Support for new btf_type_tag encoding
  2023-03-13 20:32 ` [PATCH dwarves 0/1] Support for new btf_type_tag encoding Arnaldo Carvalho de Melo
@ 2023-03-13 23:35   ` Eduard Zingerman
  0 siblings, 0 replies; 6+ messages in thread
From: Eduard Zingerman @ 2023-03-13 23:35 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: dwarves, bpf, kernel-team, ast, daniel, andrii, yhs,
	jose.marchesi, david.faust

On Mon, 2023-03-13 at 17:32 -0300, Arnaldo Carvalho de Melo wrote:
[...]
> >   @ -72998,8 +73022,8 @ struct sock {
> >           /* --- cacheline 19 boundary (1216 bytes) --- */
> >           int                        (*sk_backlog_rcv)(struct sock *, struct sk_buff *); /*  1216     8 */
> >           void                       (*sk_destruct)(struct sock *); /*  1224     8 */
> >   -       rcu *                      sk_reuseport_cb;      /*  1232     8 */
> >   -       rcu *                      sk_bpf_storage;       /*  1240     8 */
> >   +       <ERROR                    > sk_reuseport_cb;     /*  1232     8 */
> >   +       <ERROR                    > sk_bpf_storage;      /*  1240     8 */
> > 
> > (Also DWARF names refer to TYPE_TAG, not actual type name, fixed in pahole-new).
> > 
> > Warn #1: pahole-next complains about unexpected child tags generated
> > by clang, e.g.:
> > 
> >   die__create_new_tag: unspecified_type WITH children!
> >   die__create_new_base_type: DW_TAG_base_type WITH children!

Hi Arnaldo,
 
> Sure, we can remove those if the children is the expected one, leaving
> the warning maybe for debug sessions.

Yes, I handle it in the pahole-new. Warnings are not reported for
DW_TAG_LLVM_annotation children when these are applicable.

> 
> Thanks for the detailed implementation notes, references and tests
> performed, please consider breaking it up into smaller pieces or
> ellaborate on why you think can't be done.


I can split the patch into several parts,
it would be a bit stretched, though:
1. change in btf_loader / dwarves_fprintf to correctly print names for
   types with type tags;
2. consolidation of `struct btf_type_tag_type` and `struct llvm_annotation`;
3. the logic to handle "btf:type_tags";
4. support for "void __tag*" as unspecified type.

Part #3 would still be quite big, +500 LoC or something like this.
Will submit v2 today or tomorrow.

Thanks,
Eduard

> 
> Thanks!
> 
> - Arnaldo
>  
> > 
> > Performance impact
> > ------------------
> > 
> > The update to `struct tag` might raise concerns regarding memory
> > usage, additional steps in recode phase might raise concerns regarding
> > execution time. Below is statistics collected for Kernel BTF
> > generation.
> > 
> > LLVM-new / LLVM-new / pahole-new:
> > 
> > $ /usr/bin/time -v pahole -J --btf_gen_floats -j --lang_exclude=rust .tmp_vmlinux.btf
> >     ...
> > 	User time (seconds): 22.29
> > 	System time (seconds): 0.47
> > 	Percent of CPU this job got: 483%
> >     ...
> > 	Maximum resident set size (kbytes): 714524
> >     ...
> > 
> > LLVM-new / LLVM-new / pahole-next:
> > 
> > $ /usr/bin/time -v pahole -J --btf_gen_floats -j --lang_exclude=rust .tmp_vmlinux.btf
> >     ...
> > 	User time (seconds): 20.96
> > 	System time (seconds): 0.44
> > 	Percent of CPU this job got: 473%
> >     ...
> > 	Maximum resident set size (kbytes): 700848
> >     ...
> > 
> > Links & revisions
> > -----------------
> > 
> > [1] Mailing list discussion regarding `btf:type_tag`
> >     https://lore.kernel.org/bpf/87r0w9jjoq.fsf@oracle.com/
> > [2] Suggestion to use btfdiff
> >     https://lore.kernel.org/dwarves/ZAKpZGSHTvsS4r8E@kernel.org/T/#mddbfe661e339485fb2b0e706b31329b46bf61bda
> > [3] f759275c1c8e ("[AMDGPU] Regenerate sdwa-peephole.ll")
> > [4] a9498899109d ("dwarf_loader: Support for btf:type_tag")
> > [5] 49b5300f1f8f ("Merge branch 'Support stashing local kptrs with bpf_kptr_xchg'")
> > [6] LLVM changes to generate btf:type_tag, revisions stack:
> >     https://reviews.llvm.org/D143966
> >     https://reviews.llvm.org/D143967
> >     https://reviews.llvm.org/D145891
> > 
> > Eduard Zingerman (1):
> >   dwarf_loader: Support for btf:type_tag
> > 
> >  btf_encoder.c     |  13 +-
> >  btf_loader.c      |  15 +-
> >  dwarf_loader.c    | 763 +++++++++++++++++++++++++++++++++++++---------
> >  dwarves.c         |   1 +
> >  dwarves.h         |  68 +++--
> >  dwarves_fprintf.c |  13 +
> >  6 files changed, 693 insertions(+), 180 deletions(-)
> > 
> > -- 
> > 2.39.1
> > 
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH dwarves 1/1] dwarf_loader: Support for btf:type_tag
  2023-03-13  2:17 ` [PATCH dwarves 1/1] dwarf_loader: Support for btf:type_tag Eduard Zingerman
@ 2023-03-14 10:30   ` Alan Maguire
  2023-03-14 11:21     ` Eduard Zingerman
  0 siblings, 1 reply; 6+ messages in thread
From: Alan Maguire @ 2023-03-14 10:30 UTC (permalink / raw)
  To: Eduard Zingerman, dwarves, arnaldo.melo
  Cc: bpf, kernel-team, ast, daniel, andrii, yhs, jose.marchesi, david.faust

On 13/03/2023 02:17, Eduard Zingerman wrote:
> "btf:type_tag" is an DW_TAG_LLVM_annotation object that encodes
> btf_type_tag attributes in DWARF. Contrary to existing "btf_type_tag"
> it allows to associate such attributes with non-pointer types.
> When "btf:type_tag" is attached to a type it applies to this type.
> 
> For example the following C code:
> 
>     struct echo {
>       int __attribute__((btf_type_tag("__c"))) c;
>     }
> 
> Produces the following DWARF:
> 
> 0x29:   DW_TAG_structure_type
>           DW_AT_name      ("echo")
> 
> 0x40:     DW_TAG_member
>             DW_AT_name    ("c")
>             DW_AT_type    (0x8c "int")
> 
> 0x8c:   DW_TAG_base_type
>           DW_AT_name      ("int")
>           DW_AT_encoding  (DW_ATE_signed)
>           DW_AT_byte_size (0x04)
> 
> 0x90:     DW_TAG_LLVM_annotation
>             DW_AT_name    ("btf:type_tag")
>             DW_AT_const_value     ("__c")
> 
> Meaning that type 0x8c is an `int` with type tag `__c`.
> Corresponding BTF looks as follows:
> 
> [1] STRUCT 'echo'
>         ...
>         'c' type_id=8 bits_offset=128
> [4] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
> [8] TYPE_TAG '__c' type_id=4
> 
> Void pointers with type tag annotations are represented as objects
> of unspecified type with name "void", here is an example:
> 
>   struct st {
>     void __attribute__((btf_type_tag("__d"))) *d;
>   }
> 
> And corresponding DWARF:
> 
>   0x29:   DW_TAG_structure_type
>             DW_AT_name      ("st")
> 
>   0x49:     DW_TAG_member
>               DW_AT_name    ("d")
>               DW_AT_type    (0xa6 "void *")
> 
>   0xa6:   DW_TAG_pointer_type
>             DW_AT_type      (0xaf "void")
> 
>   0xaf:   DW_TAG_unspecified_type
>             DW_AT_name      ("void")
> 
>   0xb1:     DW_TAG_LLVM_annotation
>               DW_AT_name    ("btf:type_tag")
>               DW_AT_const_value     ("__d")
> 
> This commit adds support for DW_TAG_LLVM_annotation "btf:type_tag"
> attached to the following entities:
> - base types;
> - arrays;
> - pointers;
> - structs
> - unions;
> - enums;
> - typedefs.
> 
> This is achieved via the following modifications:
> - Types `struct btf_type_tag_type` and `struct llvm_annotation` are
>   consolidated as a single type `struct llvm_annotation`, in order to
>   reside in a single `annots` list associated with struct/union/enum
>   or typedef.
> - `struct unspecified_type` is added to handle `void *` types annotated
>   with `btf:type_tag`.
> - `struct tag` is extended with `annots` list.
> - DWARF load phase is modified to fill `annots` fields for the above
>   mentioned types.
> - Recode phase is split in two sub-phases:
>   - The existing `tag__recode_dwarf_type()` is executed first;
>   - Newly added `update_btf_type_tag_refs()` is executed to update
>     `->type` field of each tag if that type refers to an object with
>     `btf:type_tag` annotation. The id of the type is replaced by id
>     of the type tag.
> - When `btf:type_tag` annotations are present in compilation unit,
>   all `btf_type_tag` annotations are ignored during BTF dump.
> 
> See also:
> [1] Mailing list discussion regarding `btf:type_tag`
>     https://lore.kernel.org/bpf/87r0w9jjoq.fsf@oracle.com/
>

I know there's a v2 coming but one suggestion and one other issue below..

this is a great explanation, thanks for the details! one other thing that might help
here is specifying that the solution adopted is option 2 described in that discussion
(at least I think it is?).
 
> Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
> ---
>  btf_encoder.c     |  13 +-
>  btf_loader.c      |  15 +-
>  dwarf_loader.c    | 763 +++++++++++++++++++++++++++++++++++++---------
>  dwarves.c         |   1 +
>  dwarves.h         |  68 +++--
>  dwarves_fprintf.c |  13 +
>  6 files changed, 693 insertions(+), 180 deletions(-)
> 

<snip>

> -static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu);
> +/** Add @tuple to @ctx->mappings array, extend it if necessary. */
> +static int push_btf_type_tag_mapping(struct btf_type_tag_mapping *tuple,
> +				     struct recode_context *ctx)
> +{
> +	if (ctx->nr_allocated == ctx->nr_entries) {
> +		uint32_t new_nr = ctx->nr_allocated * 2;
> +		void *new_array = reallocarray(ctx->mappings, new_nr,
> +					       sizeof(ctx->mappings[0]));

older libcs won't have reallocarray; might be good to either replace with realloc
or define our own variant in dutil.h like was done with libbpf_reallocarray()?


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH dwarves 1/1] dwarf_loader: Support for btf:type_tag
  2023-03-14 10:30   ` Alan Maguire
@ 2023-03-14 11:21     ` Eduard Zingerman
  0 siblings, 0 replies; 6+ messages in thread
From: Eduard Zingerman @ 2023-03-14 11:21 UTC (permalink / raw)
  To: Alan Maguire, dwarves, arnaldo.melo
  Cc: bpf, kernel-team, ast, daniel, andrii, yhs, jose.marchesi, david.faust

On Tue, 2023-03-14 at 10:30 +0000, Alan Maguire wrote:
[...]
> > See also:
> > [1] Mailing list discussion regarding `btf:type_tag`
> >     https://lore.kernel.org/bpf/87r0w9jjoq.fsf@oracle.com/
> > 
> 
> I know there's a v2 coming but one suggestion and one other issue below..
> 
> this is a great explanation, thanks for the details! one other thing that might help
> here is specifying that the solution adopted is option 2 described in that discussion
> (at least I think it is?).

Hi Alan,

Yes, it's option #2, sorry should have noted it in the original message.

[...]

> > -static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu);
> > +/** Add @tuple to @ctx->mappings array, extend it if necessary. */
> > +static int push_btf_type_tag_mapping(struct btf_type_tag_mapping *tuple,
> > +				     struct recode_context *ctx)
> > +{
> > +	if (ctx->nr_allocated == ctx->nr_entries) {
> > +		uint32_t new_nr = ctx->nr_allocated * 2;
> > +		void *new_array = reallocarray(ctx->mappings, new_nr,
> > +					       sizeof(ctx->mappings[0]));
> 
> older libcs won't have reallocarray; might be good to either replace with realloc
> or define our own variant in dutil.h like was done with libbpf_reallocarray()?

Will use `realloc()`, thank you for the heads-up.

Thanks,
Eduard

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-03-14 11:22 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-13  2:17 [PATCH dwarves 0/1] Support for new btf_type_tag encoding Eduard Zingerman
2023-03-13  2:17 ` [PATCH dwarves 1/1] dwarf_loader: Support for btf:type_tag Eduard Zingerman
2023-03-14 10:30   ` Alan Maguire
2023-03-14 11:21     ` Eduard Zingerman
2023-03-13 20:32 ` [PATCH dwarves 0/1] Support for new btf_type_tag encoding Arnaldo Carvalho de Melo
2023-03-13 23:35   ` Eduard Zingerman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).