All of lore.kernel.org
 help / color / mirror / Atom feed
* [PAHOLE v5 0/5] support for DW_TAG_GNU_annotation and fixes
@ 2026-06-18  0:57 Vineet Gupta
  2026-06-18  0:57 ` [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters Vineet Gupta
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Vineet Gupta @ 2026-06-18  0:57 UTC (permalink / raw)
  To: dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust, Yonghong Song, Vineet Gupta

Hi,

This series handles gcc generated DW_TAG_GNU_annotation with the end
goal to finally enable CONFIG_PAHOLE_HAS_BTF_TAG for gcc kerenel builds.

Patches 1/5 and 5/5 are newly introduced in v5 per Alan's ask.

P.S.: I've optimistically included Reviewed-by/Acked-by from Emil and
Yonghong respectively on v4 [4] but those were before additional changes
in v5 from Alan for a few things. Please let me know if that's no longer
true.

[4] https://lore.kernel.org/bpf/20260602195512.1511013-1-vineet.gupta@linux.dev/

Thx,
-Vineet

Alan Maguire (2):
  btf_loader: Handle decl tag component_idx for parameters
  tests: Add btf_type_tag ordering test

Vineet Gupta (3):
  dwarf_loader: Extract die__add_btf_type_tag() helper [NFC]
  dwarf_loader: Add support for DW_TAG_GNU_annotation
  tests: Support GCC in pfunct-btf-decl-tags test

 btf_encoder.c                 |   1 +
 btf_loader.c                  |  46 ++++++++-
 dutil.h                       |  11 ++
 dwarf_loader.c                | 185 +++++++++++++++++++++++++++-------
 dwarves.h                     |   2 +-
 dwarves_fprintf.c             |  32 ++++--
 tests/btf_type_tag_order.sh   | 179 ++++++++++++++++++++++++++++++++
 tests/pfunct-btf-decl-tags.sh |  93 ++++++++++++-----
 8 files changed, 478 insertions(+), 71 deletions(-)
 create mode 100755 tests/btf_type_tag_order.sh

-- 
2.54.0


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

* [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters
  2026-06-18  0:57 [PAHOLE v5 0/5] support for DW_TAG_GNU_annotation and fixes Vineet Gupta
@ 2026-06-18  0:57 ` Vineet Gupta
  2026-06-18  4:46   ` Emil Tsalapatis
  2026-06-18  0:57 ` [PAHOLE v5 2/5] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 8+ messages in thread
From: Vineet Gupta @ 2026-06-18  0:57 UTC (permalink / raw)
  To: dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust, Yonghong Song, Vineet Gupta

From: Alan Maguire <alan.maguire@oracle.com>

A BTF_KIND_DECL_TAG with a non-negative component_idx applies to a
specific function parameter (or struct/union member), not to the
function itself. btf_loader.c however attached every decl tag to the
type named by btf_type->type, so parameter decl tags were recorded on
the function rather than on the parameter, and pfunct never printed
them with the parameter.

Resolve a non-negative component_idx to the corresponding parameter via
new helpers ftype__parameter()/function__parameter(), and attach the
tag there. Teach the pretty printer to emit a parameter's attributes by
factoring the function-level attribute loop into tag__attributes_fprintf()
and calling it from ftype__fprintf_parms() for each parameter.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
 btf_loader.c      | 46 +++++++++++++++++++++++++++++++++++++++++++++-
 dwarves_fprintf.c | 20 ++++++++++++++++----
 2 files changed, 61 insertions(+), 5 deletions(-)

diff --git a/btf_loader.c b/btf_loader.c
index b591219a245d..9bb52c3c5801 100644
--- a/btf_loader.c
+++ b/btf_loader.c
@@ -477,12 +477,56 @@ static struct attributes *attributes__realloc(struct attributes *attributes, con
 	return result;
 }
 
+static struct tag *ftype__parameter(const struct ftype *ftype, int component_idx)
+{
+	struct parameter *pos;
+	int idx = 0;
+
+	ftype__for_each_parameter(ftype, pos) {
+		if (idx == component_idx)
+			return &pos->tag;
+		++idx;
+	}
+
+	return NULL;
+}
+
+static struct tag *function__parameter(const struct function *func, struct cu *cu,
+				       int component_idx)
+{
+	struct tag *tag;
+
+	if (component_idx < 0)
+		return NULL;
+
+	tag = cu__type(cu, func->proto.tag.type);
+	if (tag == NULL)
+		return NULL;
+
+	return ftype__parameter(tag__ftype(tag), component_idx);
+}
+
 static int process_decl_tag(struct cu *cu, const struct btf_type *tp)
 {
+	int component_idx = btf_decl_tag(tp)->component_idx;
 	struct tag *tag = cu__type(cu, tp->type);
 	struct attributes *tmp;
 
-	if (tag == NULL)
+	if (component_idx >= 0) {
+		struct tag *func_tag = cu__function(cu, tp->type);
+
+		if (func_tag != NULL) {
+			tag = function__parameter(tag__function(func_tag), cu,
+						  component_idx);
+			if (tag == NULL) {
+				fprintf(stderr, "WARNING: BTF_KIND_DECL_TAG for unknown parameter %d in BTF id %d\n",
+					component_idx, tp->type);
+				return 0;
+			}
+		}
+	}
+
+	if (tag == NULL && component_idx < 0)
 		tag = cu__function(cu, tp->type);
 
 	if (tag == NULL)
diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c
index 1ec478c2a027..54f483244bd2 100644
--- a/dwarves_fprintf.c
+++ b/dwarves_fprintf.c
@@ -1199,6 +1199,18 @@ const char *function__prototype(const struct function *func,
 					bf, len);
 }
 
+static size_t tag__attributes_fprintf(const struct tag *tag, FILE *fp)
+{
+	size_t printed = 0;
+	int i;
+
+	if (tag->attributes)
+		for (i = 0; i < tag->attributes->cnt; ++i)
+			printed += fprintf(fp, "%s ", tag->attributes->values[i]);
+
+	return printed;
+}
+
 size_t ftype__fprintf_parms(const struct ftype *ftype,
 			    const struct cu *cu, int indent,
 			    const struct conf_fprintf *conf, FILE *fp)
@@ -1240,6 +1252,7 @@ size_t ftype__fprintf_parms(const struct ftype *ftype,
 				if (n)
 					return printed + n;
 				if (ptype->tag == DW_TAG_subroutine_type) {
+					printed += tag__attributes_fprintf(&pos->tag, fp);
 					printed +=
 					     ftype__fprintf(tag__ftype(ptype),
 							    cu, name, 0, 1, 0,
@@ -1248,12 +1261,14 @@ size_t ftype__fprintf_parms(const struct ftype *ftype,
 				}
 			}
 		} else if (type->tag == DW_TAG_subroutine_type) {
+			printed += tag__attributes_fprintf(&pos->tag, fp);
 			printed += ftype__fprintf(tag__ftype(type), cu, name,
 						  true, 0, 0, 0, conf, fp);
 			continue;
 		}
 		stype = tag__name(type, cu, sbf, sizeof(sbf), conf);
 print_it:
+		printed += tag__attributes_fprintf(&pos->tag, fp);
 		printed += fprintf(fp, "%s%s%s", stype, name ? " " : "",
 				   name ?: "");
 	}
@@ -1405,11 +1420,8 @@ static size_t function__fprintf(const struct tag *tag, const struct cu *cu,
 	struct ftype *ftype = func->btf ? tag__ftype(cu__type(cu, func->proto.tag.type)) : &func->proto;
 	size_t printed = 0;
 	bool inlined = !conf->strip_inline && function__declared_inline(func);
-	int i;
 
-	if (tag->attributes)
-		for (i = 0; i < tag->attributes->cnt; ++i)
-			printed += fprintf(fp, "%s ", tag->attributes->values[i]);
+	printed += tag__attributes_fprintf(tag, fp);
 
 	if (func->virtuality == DW_VIRTUALITY_virtual ||
 	    func->virtuality == DW_VIRTUALITY_pure_virtual)
-- 
2.54.0


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

* [PAHOLE v5 2/5] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC]
  2026-06-18  0:57 [PAHOLE v5 0/5] support for DW_TAG_GNU_annotation and fixes Vineet Gupta
  2026-06-18  0:57 ` [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters Vineet Gupta
@ 2026-06-18  0:57 ` Vineet Gupta
  2026-06-18  0:57 ` [PAHOLE v5 3/5] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Vineet Gupta @ 2026-06-18  0:57 UTC (permalink / raw)
  To: dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust, Yonghong Song, Vineet Gupta

NFC change preparing for DW_TAG_GNU_annotation support.
Extract the btf_type_tag annotation creation logic into helper
die__add_btf_type_tag().

Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
---
Changes since v [4]
 - Collected the review tags [Emil, Yonghong]

Changes since v3 [3]
 - Fix comment to kernel style [Arnaldo]

Changes since v2 [2]
 - die__add_btf_type_tag() returns pointer not error code [Alan]

Changes since v1 [1]
 - NFC reinstate some original comments

[4] https://lore.kernel.org/bpf/20260602195512.1511013-1-vineet.gupta@linux.dev/
[3] https://lore.kernel.org/bpf/20260601183511.594100-1-vineet.gupta@linux.dev/
[2] https://lore.kernel.org/bpf/20260528223616.2035618-1-vineet.gupta@linux.dev/
[1] https://lore.kernel.org/bpf/20260526181818.4159927-1-vineet.gupta@linux.dev/
---
 dwarf_loader.c | 58 +++++++++++++++++++++++++++++---------------------
 1 file changed, 34 insertions(+), 24 deletions(-)

diff --git a/dwarf_loader.c b/dwarf_loader.c
index 16fb7becffee..42a0f3f74ce3 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -1600,14 +1600,44 @@ static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *di
 	return tag;
 }
 
+static struct btf_type_tag_ptr_type *die__add_btf_type_tag(struct btf_type_tag_ptr_type *tag,
+							    Dwarf_Die *die, Dwarf_Die *adie,
+							    struct cu *cu, struct conf_load *conf)
+{
+	struct btf_type_tag_type *annot;
+	uint32_t id;
+
+	if (tag == NULL) {
+		tag = die__create_new_btf_type_tag_ptr_type(die, cu);
+		if (!tag)
+			return NULL;
+	}
+
+	annot = die__create_new_btf_type_tag_type(adie, cu, conf);
+	if (annot == NULL)
+		return NULL;
+
+	if (cu__table_add_tag(cu, &annot->tag, &id) < 0)
+		return NULL;
+
+	struct dwarf_tag *dtag = tag__dwarf(&annot->tag);
+	dtag->small_id = id;
+	cu__hash(cu, &annot->tag);
+
+	/*
+	 * Prepends: for annotations tag1 -> tag2 -> tag3,
+	 * the tag->tags list ends up as tag3 -> tag2 -> tag1.
+	 */
+	list_add(&annot->node, &tag->tags);
+	return tag;
+}
+
 static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu,
 					       struct conf_load *conf)
 {
 	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
@@ -1627,29 +1657,9 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu,
 		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)
+		tag = die__add_btf_type_tag(tag, die, cdie, cu, conf);
+		if (tag == NULL)
 			return NULL;
-
-		struct dwarf_tag *dtag = tag__dwarf(&annot->tag);
-		dtag->small_id = id;
-		cu__hash(cu, &annot->tag);
-
-		/* 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);
 
 	return tag ? &tag->tag : tag__new(die, cu);
-- 
2.54.0


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

* [PAHOLE v5 3/5] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-18  0:57 [PAHOLE v5 0/5] support for DW_TAG_GNU_annotation and fixes Vineet Gupta
  2026-06-18  0:57 ` [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters Vineet Gupta
  2026-06-18  0:57 ` [PAHOLE v5 2/5] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta
@ 2026-06-18  0:57 ` Vineet Gupta
  2026-06-18  0:57 ` [PAHOLE v5 4/5] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
  2026-06-18  0:57 ` [PAHOLE v5 5/5] tests: Add btf_type_tag ordering test Vineet Gupta
  4 siblings, 0 replies; 8+ messages in thread
From: Vineet Gupta @ 2026-06-18  0:57 UTC (permalink / raw)
  To: dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust, Yonghong Song, Vineet Gupta

gcc 16 was the first release to support DW_TAG_GNU_annotations and this
patch enables the same in pahole. Bulk of changes are dwarf_loader but
btf_encoder also gains support with minimal changes.

GCC encodes btf_type_tag and btf_decl_tag annotations differently from
LLVM. While LLVM uses DW_TAG_LLVM_annotation (0x6000) as child DIEs,
GCC uses DW_TAG_GNU_annotation (0x6001) as standalone sibling DIEs
referenced via DW_AT_GNU_annotation (0x2139) attributes, with chaining
through the same attribute on annotation DIEs themselves.

Handle both encoding styles:

For btf_type_tag (pointer annotations):
- Recognize DW_TAG_GNU_annotation alongside DW_TAG_LLVM_annotation in
  child annotation scanning.
- Follow DW_AT_GNU_annotation attribute chains on pointer types for
  GCC-style btf_type_tag resolution.
- Normalize DW_TAG_GNU_annotation to DW_TAG_LLVM_annotation in the
  internal representation so downstream code works unchanged.
- Preserve btf_type_tag emission order across both formats: LLVM child
  DIEs come in source order and are prepended (reversed) into the tag
  list, while a GCC DW_AT_GNU_annotation chain is already in BTF order
  and is appended. die__add_btf_type_tag() takes a 'prepend' argument
  to select between the two.

For btf_decl_tag (function/struct/member annotations):
- Add add_gnu_annotation_chain() to follow DW_AT_GNU_annotation
  attribute chains on function, struct, and member DIEs.
- GCC puts DW_AT_GNU_annotation on the function/struct DIE itself
  (not as child DIEs), referencing sibling annotation DIEs that chain
  via the same attribute.
- Follow DW_AT_GNU_annotation chains on formal parameter DIEs so that
  GCC-style parameter decl tags are loaded too.

Also:
- Silently skip standalone DW_TAG_GNU_annotation DIEs at CU level.
- Add tag__is_annotation() helper macro for annotation tag checks.
- Rename add_llvm_annotation -> add_tag_annotation,
  skip_llvm_annotations -> skip_tag_annotations since these now
  handle both LLVM and GNU annotation formats.

Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
---
Changes since v4 [4]
 - Handle gcc-style function paramter tags [Alan]
 - Handle multi tag ordering correctly for LLVM and GCC [Alan]
 - Collected the review tags [Emil, Yonghong]

Changes since v3 [3]
 - Add helper tag__is_annotation [Emil]
 - Rename some functions containing "llvm" as they are common to LLVM/GCC tags [Emil]
 - Deduplicate checks before loop and inside loop in add_gnu_annotation_chain() and check_gnu_attr [Arnaldo]
 - Fix some typos and move some comments [Emi]

Changes since v2 [2]
 - Removed loop detection logic [Alan]
 - Move test changes to different patch [Alan]

Changes since v1 [1]
 - NFC Reduce indentation with early exits (Alexei offlist)

[4] https://lore.kernel.org/bpf/20260602195512.1511013-2-vineet.gupta@linux.dev/
[3] https://lore.kernel.org/bpf/20260601183511.594100-2-vineet.gupta@linux.dev/
[2] https://lore.kernel.org/bpf/20260528223616.2035618-2-vineet.gupta@linux.dev/
[1] https://lore.kernel.org/bpf/20260526181818.4159927-2-vineet.gupta@linux.dev/
---
 btf_encoder.c     |   1 +
 dutil.h           |  11 ++++
 dwarf_loader.c    | 141 +++++++++++++++++++++++++++++++++++++++-------
 dwarves.h         |   2 +-
 dwarves_fprintf.c |  12 ++--
 5 files changed, 141 insertions(+), 26 deletions(-)

diff --git a/btf_encoder.c b/btf_encoder.c
index 633bc6162ce0..d5af706d7638 100644
--- a/btf_encoder.c
+++ b/btf_encoder.c
@@ -1831,6 +1831,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:
+	case DW_TAG_GNU_annotation:
 		name = tag__btf_type_tag(tag)->value;
 		return btf_encoder__add_ref_type(encoder, BTF_KIND_TYPE_TAG, ref_type_id, name, false);
 	case DW_TAG_structure_type:
diff --git a/dutil.h b/dutil.h
index ff78aa6dfd10..be02c68cf4a7 100644
--- a/dutil.h
+++ b/dutil.h
@@ -35,6 +35,17 @@
 #define DW_TAG_LLVM_annotation 0x6000
 #endif
 
+#ifndef DW_TAG_GNU_annotation
+#define DW_TAG_GNU_annotation 0x6001
+#endif
+
+#ifndef DW_AT_GNU_annotation
+#define DW_AT_GNU_annotation 0x2139
+#endif
+
+#define tag__is_annotation(tag) \
+	((tag) == DW_TAG_LLVM_annotation || (tag) == DW_TAG_GNU_annotation)
+
 static inline __attribute__((const)) bool is_power_of_2(unsigned long n)
 {
         return (n != 0 && ((n & (n - 1)) == 0));
diff --git a/dwarf_loader.c b/dwarf_loader.c
index 42a0f3f74ce3..8ce34cbb8ed6 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -908,7 +908,12 @@ 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,
+static bool die__tag_is_annotation(Dwarf_Die *die)
+{
+	return tag__is_annotation(dwarf_tag(die));
+}
+
+static int add_tag_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf,
 			       struct list_head *head)
 {
 	struct llvm_annotation *annot;
@@ -943,16 +948,48 @@ 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);
-			if (ret)
-				return ret;
-		}
+		if (!die__tag_is_annotation(die))
+			continue;
+
+		ret = add_tag_annotation(die, component_idx, conf, head);
+		if (ret)
+			return ret;
 	} while (dwarf_siblingof(die, die) == 0);
 
 	return 0;
 }
 
+/*
+ * Handle gcc style btf_decl_tag annotations attached directly to a DIE via
+ * the DW_AT_GNU_annotation attribute chain: functions, function parameters
+ * and struct/union members.
+ *
+ * Note: gcc does not yet emit decl tags for a whole struct/union or for a
+ * typedef, so calling this on such a DIE is harmless - the attribute is
+ * simply absent and the chain is empty.
+ *
+ * Pointers (btf_type_tag) are handled separately, inline in
+ * die__create_new_pointer_tag().
+ */
+static int add_gnu_annotation_chain(Dwarf_Die *die, int component_idx,
+				    struct conf_load *conf, struct list_head *head)
+{
+	Dwarf_Attribute attr;
+	Dwarf_Die annot_die;
+
+	while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL &&
+	       dwarf_formref_die(&attr, &annot_die) != NULL &&
+	       dwarf_tag(&annot_die) == DW_TAG_GNU_annotation) {
+		int ret = add_tag_annotation(&annot_die, component_idx, conf, head);
+		if (ret)
+			return ret;
+
+		die = &annot_die;
+	}
+
+	return 0;
+}
+
 int class_member__dwarf_recode_bitfield(struct class_member *member,
 					struct cu *cu)
 {
@@ -1596,13 +1633,16 @@ static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *di
 		return NULL;
 
 	tag__init(&tag->tag, cu, die);
+	/* Normalize DW_TAG_GNU_annotation to DW_TAG_LLVM_annotation internally */
+	tag->tag.tag = DW_TAG_LLVM_annotation;
 	tag->value = attr_string(die, DW_AT_const_value, conf);
 	return tag;
 }
 
 static struct btf_type_tag_ptr_type *die__add_btf_type_tag(struct btf_type_tag_ptr_type *tag,
 							    Dwarf_Die *die, Dwarf_Die *adie,
-							    struct cu *cu, struct conf_load *conf)
+							    struct cu *cu, struct conf_load *conf,
+							    bool prepend)
 {
 	struct btf_type_tag_type *annot;
 	uint32_t id;
@@ -1625,10 +1665,23 @@ static struct btf_type_tag_ptr_type *die__add_btf_type_tag(struct btf_type_tag_p
 	cu__hash(cu, &annot->tag);
 
 	/*
-	 * Prepends: for annotations tag1 -> tag2 -> tag3,
-	 * the tag->tags list ends up as tag3 -> tag2 -> tag1.
+	 * Consider:
+	 *   struct sample __tag(outer) __tag(inner) *global_ptr;
+	 * The BTF chain should describe the pointer as:
+	 *   PTR -> TYPE_TAG "inner" -> TYPE_TAG "outer" -> STRUCT "sample"
+	 * i.e. the tag nearest the '*' wraps the pointee first.
+	 *
+	 * LLVM emits the child annotation DIEs in source order (outer, inner),
+	 * so we prepend to end up with inner -> outer.
+	 *
+	 * GCC's DW_AT_GNU_annotation chain is already walked from the outermost
+	 * BTF wrapper (inner) towards the pointee, so we preserve that order by
+	 * appending.
 	 */
-	list_add(&annot->node, &tag->tags);
+	if (prepend)
+		list_add(&annot->node, &tag->tags);
+	else
+		list_add_tail(&annot->node, &tag->tags);
 	return tag;
 }
 
@@ -1637,19 +1690,21 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu,
 {
 	struct btf_type_tag_ptr_type *tag = NULL;
 	Dwarf_Die *cdie, child;
+	Dwarf_Attribute attr;
+	Dwarf_Die annot_die;
 	const char *name;
 
-	/* 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)
+	/* If skipping btf_type_tag encoding, just create a new tag, return */
+	if (conf->skip_encoding_btf_type_tag)
 		return tag__new(die, cu);
 
-	/* Otherwise, check DW_TAG_LLVM_annotation child tags */
+	if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0)
+		goto check_gnu_attr;
+
+	/* Handle LLVM style annotation tags if present */
 	cdie = &child;
 	do {
-		if (dwarf_tag(cdie) != DW_TAG_LLVM_annotation)
+		if (!die__tag_is_annotation(cdie))
 			continue;
 
 		/* Only check btf_type_tag annotations */
@@ -1657,11 +1712,33 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu,
 		if (strcmp(name, "btf_type_tag") != 0)
 			continue;
 
-		tag = die__add_btf_type_tag(tag, die, cdie, cu, conf);
+		/* LLVM child DIEs are in source order; prepend to reverse. */
+		tag = die__add_btf_type_tag(tag, die, cdie, cu, conf, true);
 		if (tag == NULL)
 			return NULL;
 	} while (dwarf_siblingof(cdie, cdie) == 0);
 
+check_gnu_attr:
+	if (tag != NULL)
+		goto out;
+
+	/* Handle GCC-style DW_AT_GNU_annotation attribute */
+	while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL &&
+	       dwarf_formref_die(&attr, &annot_die) != NULL &&
+	       dwarf_tag(&annot_die) == DW_TAG_GNU_annotation) {
+		name = attr_string(&annot_die, DW_AT_name, conf);
+		if (strcmp(name, "btf_type_tag") != 0)
+			break;
+
+		/* GCC chain is already in BTF order; append to preserve it. */
+		tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf, false);
+		if (tag == NULL)
+			return NULL;
+
+		die = &annot_die;
+	}
+
+out:
 	return tag ? &tag->tag : tag__new(die, cu);
 }
 
@@ -1690,6 +1767,12 @@ static struct tag *die__create_new_class(Dwarf_Die *die, struct cu *cu, struct c
 		}
 	}
 
+	if (class != NULL &&
+	    add_gnu_annotation_chain(die, -1, conf, &class->type.namespace.annots) != 0) {
+		class__delete(class, cu);
+		class = NULL;
+	}
+
 	return class ? &class->type.namespace.tag : NULL;
 }
 
@@ -1822,6 +1905,8 @@ static struct tag *die__create_new_parameter(Dwarf_Die *die,
 		if (param_idx >= 0) {
 			if (add_child_llvm_annotations(die, param_idx, conf, &(tag__function(&ftype->tag)->annots)))
 				return NULL;
+			if (add_gnu_annotation_chain(die, param_idx, conf, &(tag__function(&ftype->tag)->annots)))
+				return NULL;
 		}
 	} else {
 		/*
@@ -2051,11 +2136,14 @@ static int die__process_class(Dwarf_Die *die, struct type *class,
 			cu__hash(cu, &member->tag);
 			if (add_child_llvm_annotations(die, member_idx, conf, &class->namespace.annots))
 				return -ENOMEM;
+			if (add_gnu_annotation_chain(die, member_idx, conf, &class->namespace.annots))
+				return -ENOMEM;
 			member_idx++;
 		}
 			continue;
 		case DW_TAG_LLVM_annotation:
-			if (add_llvm_annotation(die, -1, conf, &class->namespace.annots))
+		case DW_TAG_GNU_annotation:
+			if (add_tag_annotation(die, -1, conf, &class->namespace.annots))
 				return -ENOMEM;
 			continue;
 		default: {
@@ -2360,7 +2448,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)))
+		case DW_TAG_GNU_annotation:
+			if (add_tag_annotation(die, -1, conf, &(tag__function(&ftype->tag)->annots)))
 				goto out_enomem;
 			continue;
 		default:
@@ -2408,6 +2497,12 @@ static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *cu, struc
 		function = NULL;
 	}
 
+	if (function != NULL &&
+	    add_gnu_annotation_chain(die, -1, conf, &function->annots) != 0) {
+		function__delete(function, cu);
+		function = NULL;
+	}
+
 	return function ? &function->proto.tag : NULL;
 }
 
@@ -2469,6 +2564,9 @@ static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu,
 		 */
 		tag = &unsupported_tag;
 		break;
+	case DW_TAG_GNU_annotation:
+		tag = &unsupported_tag;
+		break;
 	case DW_TAG_label:
 		if (conf->ignore_labels)
 			tag = &unsupported_tag; // callers will assume conf->ignore_labels is true
@@ -2494,7 +2592,8 @@ static int die__process_unit(Dwarf_Die *die, struct cu *cu, struct conf_load *co
 			// XXX special case DW_TAG_dwarf_procedure, appears when looking at a recent ~/bin/perf
 			// Investigate later how to properly support this...
 			if (dwarf_tag(die) != DW_TAG_dwarf_procedure &&
-			    dwarf_tag(die) != DW_TAG_label) // conf->ignore_labels == true, see die__process_tag()
+			    dwarf_tag(die) != DW_TAG_label && // conf->ignore_labels == true, see die__process_tag()
+			    dwarf_tag(die) != DW_TAG_GNU_annotation)
 				tag__print_not_supported(die);
 			continue;
 		}
diff --git a/dwarves.h b/dwarves.h
index 5ec16e750e83..75c311a2f8ee 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -670,7 +670,7 @@ static inline int tag__is_tag_type(const struct tag *tag)
 	       tag->tag == DW_TAG_volatile_type ||
 	       tag->tag == DW_TAG_atomic_type ||
 	       tag->tag == DW_TAG_unspecified_type ||
-	       tag->tag == DW_TAG_LLVM_annotation;
+	       tag__is_annotation(tag->tag);
 }
 
 static inline const char *tag__decl_file(const struct tag *tag,
diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c
index 54f483244bd2..ab1c381db646 100644
--- a/dwarves_fprintf.c
+++ b/dwarves_fprintf.c
@@ -140,6 +140,8 @@ const char *dwarf_tag_name(const uint32_t tag)
 		return dwarf_gnu_tag_names[tag - DW_TAG_MIPS_loop];
 	else if (tag == DW_TAG_LLVM_annotation)
 		return "LLVM_annotation";
+	else if (tag == DW_TAG_GNU_annotation)
+		return "GNU_annotation";
 	return "INVALID";
 }
 
@@ -658,6 +660,7 @@ static const char *__tag__name(const struct tag *tag, const struct cu *cu,
 		snprintf(bf, len, "%s", variable__name(tag__variable(tag)));
 		break;
 	case DW_TAG_LLVM_annotation:
+	case DW_TAG_GNU_annotation:
 		type = cu__type(cu, tag->type);
 		if (type == NULL && tag->type != 0)
 			tag__id_not_found_snprintf(bf, len, tag->type);
@@ -723,7 +726,7 @@ static size_t type__fprintf_stats(struct type *type, const struct cu *cu,
 	return printed;
 }
 
-static type_id_t skip_llvm_annotations(const struct cu *cu, type_id_t id)
+static type_id_t skip_tag_annotations(const struct cu *cu, type_id_t id)
 {
 	struct tag *type;
 
@@ -731,7 +734,7 @@ static type_id_t skip_llvm_annotations(const struct cu *cu, type_id_t id)
 		if (id == 0)
 			break;
 		type = cu__type(cu, id);
-		if (type == NULL || type->tag != DW_TAG_LLVM_annotation || type->type == id)
+		if (type == NULL || !tag__is_annotation(type->tag) || type->type == id)
 			break;
 		id = type->type;
 	}
@@ -838,7 +841,7 @@ inner_struct:
 next_type:
 	switch (type->tag) {
 	case DW_TAG_pointer_type: {
-		type_id_t ptype_id = skip_llvm_annotations(cu, type->type);
+		type_id_t ptype_id = skip_tag_annotations(cu, type->type);
 
 		if (ptype_id != 0) {
 			int n;
@@ -936,7 +939,8 @@ print_modifier: {
 		else
 			printed += enumeration__fprintf(type, &tconf, fp);
 		break;
-	case DW_TAG_LLVM_annotation: {
+	case DW_TAG_LLVM_annotation:
+	case DW_TAG_GNU_annotation: {
 		struct tag *ttype = cu__type(cu, type->type);
 		if (ttype) {
 			type = ttype;
-- 
2.54.0


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

* [PAHOLE v5 4/5] tests: Support GCC in pfunct-btf-decl-tags test
  2026-06-18  0:57 [PAHOLE v5 0/5] support for DW_TAG_GNU_annotation and fixes Vineet Gupta
                   ` (2 preceding siblings ...)
  2026-06-18  0:57 ` [PAHOLE v5 3/5] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
@ 2026-06-18  0:57 ` Vineet Gupta
  2026-06-18  0:57 ` [PAHOLE v5 5/5] tests: Add btf_type_tag ordering test Vineet Gupta
  4 siblings, 0 replies; 8+ messages in thread
From: Vineet Gupta @ 2026-06-18  0:57 UTC (permalink / raw)
  To: dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust, Yonghong Song, Vineet Gupta

GCC 16+ supports btf_decl_tag via DW_TAG_GNU_annotation. Update the
test to run with both GCC (>= 16) and clang when available, instead
of requiring clang only.

Compile the clang case natively and encode BTF with "pahole -J" (like
the GCC case) rather than emitting BPF-target BTF directly, so both
compilers exercise the same DWARF -> BTF path.

Add a function with a parameter decl tag (qux) to cover decl tags that
carry a component_idx, and make the awk post-processing pass through
lines that have no leading tags.

Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
---
Changes since v4 [4]
 - Test function param decl tag [Alan]
 - Missing "pahole -j" for clang [Yonghong]
 - Collected the review tags [Emil, Yonghong]

[4] https://lore.kernel.org/bpf/20260602195512.1511013-3-vineet.gupta@linux.dev/
---

 tests/pfunct-btf-decl-tags.sh | 93 ++++++++++++++++++++++++++---------
 1 file changed, 70 insertions(+), 23 deletions(-)

diff --git a/tests/pfunct-btf-decl-tags.sh b/tests/pfunct-btf-decl-tags.sh
index 35884b4e8687..520148b2ccef 100755
--- a/tests/pfunct-btf-decl-tags.sh
+++ b/tests/pfunct-btf-decl-tags.sh
@@ -6,43 +6,60 @@
 source test_lib.sh
 
 outdir=$(make_tmpdir)
-tmpobj=$(make_tmpobj)
 
 # Comment this out to save test data.
 trap cleanup EXIT
 
 title_log "Check that pfunct can print btf_decl_tags read from BTF."
 
-# gcc now also supports decl tags as of gcc commit 43dcea48b8c,
-# in upstream version 16.
-# UPTODO: add a check here for that.
+# gcc 16+ supports decl tags via DW_TAG_GNU_annotation (gcc commit ac7027f180b).
 
+GCC=${GCC:-gcc}
 CLANG=${CLANG:-clang}
-if ! command -v $CLANG > /dev/null; then
-	error_log "Need clang for test $0"
+
+use_gcc=0
+if command -v $GCC > /dev/null; then
+	gcc_ver=$($GCC -dumpversion 2>/dev/null | cut -d. -f1)
+	if [ "$gcc_ver" -ge 16 ] 2>/dev/null; then
+		use_gcc=1
+	fi
+fi
+
+use_clang=0
+if command -v $CLANG > /dev/null; then
+	use_clang=1
+fi
+
+if [ "$use_gcc" -eq 0 ] && [ "$use_clang" -eq 0 ]; then
+	error_log "Need gcc >= 16 or clang for test $0"
 	test_fail
 fi
 
-(cat <<EOF
+src=$(cat <<EOF
 #define __tag(x) __attribute__((btf_decl_tag(#x)))
 
 __tag(a) __tag(b) __tag(c) void foo(void) {}
 __tag(a) __tag(b)          void bar(void) {}
 __tag(a)                   void buz(void) {}
+void qux(int __tag(param_a) arg) {}
 
 EOF
-) | $CLANG --target=bpf -c -g -x c -o $tmpobj -
+)
 
 # tags order is not guaranteed
 sort_tags=$(cat <<EOF
 {
-match(\$0,/^(.*) (void .*)/,tags_and_proto);
-tags  = tags_and_proto[1];
-proto = tags_and_proto[2];
-split(tags, tags_arr ,/ /);
-asort(tags_arr);
-for (t in tags_arr) printf "%s ", tags_arr[t];
-print proto;
+delete tags_arr;
+if (match(\$0,/^(.*) (void .*)/,tags_and_proto)) {
+	tags  = tags_and_proto[1];
+	proto = tags_and_proto[2];
+	split(tags, tags_arr ,/ /);
+	asort(tags_arr);
+	for (t in tags_arr) printf "%s ", tags_arr[t];
+	print proto;
+} else {
+	print \$0;
+}
 }
 EOF
 )
@@ -51,19 +68,49 @@ expected=$(cat <<EOF
 a b c void foo(void);
 a b void bar(void);
 a void buz(void);
+void qux(param_a int arg);
 EOF
 )
 
-out=$(pfunct -P -F btf $tmpobj | awk "$sort_tags" | sort)
-d=$(diff -u <(echo "$expected") <(echo "$out"))
+run_test() {
+	local compiler=$1
+	local tmpobj=$2
+
+	info_log "Testing with $compiler"
+	out=$(pfunct -P -F btf $tmpobj | awk "$sort_tags" | sort)
+	d=$(diff -u <(echo "$expected") <(echo "$out"))
+
+	if [[ "$d" == "" ]]; then
+		info_log "  passed"
+		return 0
+	else
+		error_log "pfunct output does not match expected ($compiler):"
+		info_log "$d"
+		info_log
+		info_log "Complete output:"
+		info_log "$out"
+		return 1
+	fi
+}
+
+failed=0
+
+if [ "$use_gcc" -eq 1 ]; then
+	tmpobj=$(make_tmpobj)
+	echo "$src" | $GCC -c -g -x c -o $tmpobj - 2>/dev/null
+	pahole -J $tmpobj 2>/dev/null
+	run_test "$GCC (version $gcc_ver)" "$tmpobj" || failed=1
+fi
+
+if [ "$use_clang" -eq 1 ]; then
+	tmpobj=$(make_tmpobj)
+	echo "$src" | $CLANG -c -g -x c -o $tmpobj -
+	pahole -J $tmpobj 2>/dev/null
+	run_test "$CLANG" "$tmpobj" || failed=1
+fi
 
-if [[ "$d" == "" ]]; then
+if [ "$failed" -eq 0 ]; then
 	test_pass
 else
-	error_log "pfunct output does not match expected:"
-	info_log "$d"
-	info_log
-	info_log "Complete output:"
-	info_log "$out"
 	test_fail
 fi
-- 
2.54.0


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

* [PAHOLE v5 5/5] tests: Add btf_type_tag ordering test
  2026-06-18  0:57 [PAHOLE v5 0/5] support for DW_TAG_GNU_annotation and fixes Vineet Gupta
                   ` (3 preceding siblings ...)
  2026-06-18  0:57 ` [PAHOLE v5 4/5] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
@ 2026-06-18  0:57 ` Vineet Gupta
  2026-06-18  4:47   ` Emil Tsalapatis
  4 siblings, 1 reply; 8+ messages in thread
From: Vineet Gupta @ 2026-06-18  0:57 UTC (permalink / raw)
  To: dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust, Yonghong Song, Vineet Gupta

From: Alan Maguire <alan.maguire@oracle.com>

Add a test that compiles a pointer carrying two btf_type_tags
(__tag(outer) __tag(inner)) and checks the BTF that pahole emits to
ensure the tag chain is PTR -> TYPE_TAG inner -> TYPE_TAG outer ->
struct sample, i.e. the innermost tag is closest to the pointer.

This exercises the type_tag ordering for both encodings: LLVM emits the
annotations as child DIEs in source order (reversed when building the
tag list), while GCC chains them via DW_AT_GNU_annotation already in BTF
order. The test runs with gcc and clang when they support btf_type_tag,
and is skipped when bpftool is unavailable.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
 tests/btf_type_tag_order.sh | 179 ++++++++++++++++++++++++++++++++++++
 1 file changed, 179 insertions(+)
 create mode 100755 tests/btf_type_tag_order.sh

diff --git a/tests/btf_type_tag_order.sh b/tests/btf_type_tag_order.sh
new file mode 100755
index 000000000000..09d1ac346000
--- /dev/null
+++ b/tests/btf_type_tag_order.sh
@@ -0,0 +1,179 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-only
+
+# Check that pahole preserves btf_type_tag order when emitting BTF from DWARF.
+
+source test_lib.sh
+
+outdir=$(make_tmpdir)
+
+# Comment this out to save test data.
+trap cleanup EXIT
+
+title_log "Check BTF type tag order."
+
+GCC=${GCC:-gcc}
+CLANG=${CLANG:-clang}
+PAHOLE=${PAHOLE:-pahole}
+BPFTOOL=${BPFTOOL:-bpftool}
+
+if ! command -v "$BPFTOOL" > /dev/null; then
+	info_log "skip: bpftool not available"
+	test_skip
+fi
+
+compiler_has_btf_type_tag()
+{
+	local compiler=$1
+
+	if ! command -v "$compiler" > /dev/null; then
+		return 1
+	fi
+
+	"$compiler" -x c -E -P - <<'EOF' 2>/dev/null | grep -qx 1
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#endif
+#if __has_attribute(btf_type_tag)
+1
+#else
+0
+#endif
+EOF
+}
+
+use_gcc=0
+if compiler_has_btf_type_tag "$GCC"; then
+	use_gcc=1
+fi
+
+use_clang=0
+if compiler_has_btf_type_tag "$CLANG"; then
+	use_clang=1
+fi
+
+if [ "$use_gcc" -eq 0 ] && [ "$use_clang" -eq 0 ]; then
+	error_log "Need gcc or clang with btf_type_tag support for test $0"
+	test_fail
+fi
+
+src=$(cat <<EOF
+#define __tag(x) __attribute__((btf_type_tag(#x)))
+
+struct sample {
+	int value;
+};
+
+struct sample __tag(outer) __tag(inner) *global_ptr;
+
+EOF
+)
+
+check_type_tag_order()
+{
+	local btf=$1
+	local dump
+
+	if ! dump=$("$BPFTOOL" btf dump file "$btf"); then
+		return 1
+	fi
+
+	printf '%s\n' "$dump" | awk '
+	function parse_id(line, m) {
+		if (match(line, /^\[([0-9]+)\]/, m))
+			return m[1]
+		return 0
+	}
+	function parse_name(line, m) {
+		if (match(line, /\047([^\047]*)\047/, m))
+			return m[1]
+		return ""
+	}
+	function parse_type(line, m) {
+		if (match(line, /type_id=([0-9]+)/, m))
+			return m[1]
+		return 0
+	}
+	function check_ptr(ptr, id, tags, seen) {
+		id = type[ptr]
+		while (id != 0 && !seen[id]) {
+			seen[id] = 1
+			if (kind[id] == "TYPE_TAG") {
+				tags = tags (tags == "" ? "" : " -> ") name[id]
+				id = type[id]
+				continue
+			}
+			if (kind[id] == "STRUCT" && name[id] == "sample") {
+				if (tags == "inner -> outer")
+					exit 0
+				candidates = candidates (candidates == "" ? "" : ", ") tags
+			}
+			return
+		}
+	}
+	/^\[[0-9]+\]/ {
+		id = parse_id($0)
+		kind[id] = $2
+		name[id] = parse_name($0)
+		type[id] = parse_type($0)
+		if (kind[id] == "PTR")
+			ptrs[++nr_ptrs] = id
+	}
+	END {
+		for (i = 1; i <= nr_ptrs; i++)
+			check_ptr(ptrs[i])
+		if (candidates != "") {
+			print "type tag order mismatch; expected inner -> outer, found " candidates > "/dev/stderr"
+			exit 1
+		}
+		print "could not find tagged pointer to struct sample" > "/dev/stderr"
+		exit 1
+	}'
+}
+
+run_test()
+{
+	local compiler=$1
+	local tmpobj=$2
+	local btf=$3
+
+	info_log "Testing with $compiler"
+
+	if ! echo "$src" | "$compiler" -g -c -x c -o "$tmpobj" - 2>/dev/null; then
+		error_log "Could not compile type tag order test with $compiler"
+		return 1
+	fi
+
+	if ! "$PAHOLE" --btf_features=+type_tag --btf_encode_detached="$btf" "$tmpobj" 2>/dev/null; then
+		error_log "Could not encode BTF for $tmpobj"
+		return 1
+	fi
+
+	if check_type_tag_order "$btf"; then
+		info_log "  passed"
+		return 0
+	fi
+
+	error_log "BTF type tag order does not match expected order ($compiler)"
+	return 1
+}
+
+failed=0
+
+if [ "$use_gcc" -eq 1 ]; then
+	tmpobj=$(make_tmpobj)
+	btf=${tmpobj%.o}.btf
+	run_test "$GCC" "$tmpobj" "$btf" || failed=1
+fi
+
+if [ "$use_clang" -eq 1 ]; then
+	tmpobj=$(make_tmpobj)
+	btf=${tmpobj%.o}.btf
+	run_test "$CLANG" "$tmpobj" "$btf" || failed=1
+fi
+
+if [ "$failed" -eq 0 ]; then
+	test_pass
+else
+	test_fail
+fi
-- 
2.54.0


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

* Re: [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters
  2026-06-18  0:57 ` [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters Vineet Gupta
@ 2026-06-18  4:46   ` Emil Tsalapatis
  0 siblings, 0 replies; 8+ messages in thread
From: Emil Tsalapatis @ 2026-06-18  4:46 UTC (permalink / raw)
  To: Vineet Gupta, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust, Yonghong Song

On Wed Jun 17, 2026 at 8:57 PM EDT, Vineet Gupta wrote:
> From: Alan Maguire <alan.maguire@oracle.com>
>
> A BTF_KIND_DECL_TAG with a non-negative component_idx applies to a
> specific function parameter (or struct/union member), not to the
> function itself. btf_loader.c however attached every decl tag to the
> type named by btf_type->type, so parameter decl tags were recorded on
> the function rather than on the parameter, and pfunct never printed
> them with the parameter.
>
> Resolve a non-negative component_idx to the corresponding parameter via
> new helpers ftype__parameter()/function__parameter(), and attach the
> tag there. Teach the pretty printer to emit a parameter's attributes by
> factoring the function-level attribute loop into tag__attributes_fprintf()
> and calling it from ftype__fprintf_parms() for each parameter.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>

With one nit:

Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>

(Also feel free to keep the tags for the other 3 patches since the 
changes are minimal).

</SNIP>

> +	if (component_idx >= 0) {
> +		struct tag *func_tag = cu__function(cu, tp->type);
> +
> +		if (func_tag != NULL) {
> +			tag = function__parameter(tag__function(func_tag), cu,
> +						  component_idx);
> +			if (tag == NULL) {
> +				fprintf(stderr, "WARNING: BTF_KIND_DECL_TAG for unknown parameter %d in BTF id %d\n",
> +					component_idx, tp->type);
> +				return 0;
> +			}
> +		}
> +	}
> +
> +	if (tag == NULL && component_idx < 0)
>  		tag = cu__function(cu, tp->type);

Nit, I think this can be simplified as follows:

tag = cu__function(cu, tp->type);
if (component_idx >= 0 && tag != NULL) {
	tag = function__parameter(tag__function(tag), cu, component_idx);
	if (tag == NULL) {
		fprintf(stderr, "WARNING: BTF_KIND_DECL_TAG for unknown parameter %d in BTF id %d\n",
			component_idx, tp->type);
		return 0;
	}
}

<SNIP>

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

* Re: [PAHOLE v5 5/5] tests: Add btf_type_tag ordering test
  2026-06-18  0:57 ` [PAHOLE v5 5/5] tests: Add btf_type_tag ordering test Vineet Gupta
@ 2026-06-18  4:47   ` Emil Tsalapatis
  0 siblings, 0 replies; 8+ messages in thread
From: Emil Tsalapatis @ 2026-06-18  4:47 UTC (permalink / raw)
  To: Vineet Gupta, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust, Yonghong Song

On Wed Jun 17, 2026 at 8:57 PM EDT, Vineet Gupta wrote:
> From: Alan Maguire <alan.maguire@oracle.com>
>
> Add a test that compiles a pointer carrying two btf_type_tags
> (__tag(outer) __tag(inner)) and checks the BTF that pahole emits to
> ensure the tag chain is PTR -> TYPE_TAG inner -> TYPE_TAG outer ->
> struct sample, i.e. the innermost tag is closest to the pointer.
>
> This exercises the type_tag ordering for both encodings: LLVM emits the
> annotations as child DIEs in source order (reversed when building the
> tag list), while GCC chains them via DW_AT_GNU_annotation already in BTF
> order. The test runs with gcc and clang when they support btf_type_tag,
> and is skipped when bpftool is unavailable.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
> ---

Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>

>  tests/btf_type_tag_order.sh | 179 ++++++++++++++++++++++++++++++++++++
>  1 file changed, 179 insertions(+)
>  create mode 100755 tests/btf_type_tag_order.sh
>
> diff --git a/tests/btf_type_tag_order.sh b/tests/btf_type_tag_order.sh
> new file mode 100755
> index 000000000000..09d1ac346000
> --- /dev/null
> +++ b/tests/btf_type_tag_order.sh
> @@ -0,0 +1,179 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +# Check that pahole preserves btf_type_tag order when emitting BTF from DWARF.
> +
> +source test_lib.sh
> +
> +outdir=$(make_tmpdir)
> +
> +# Comment this out to save test data.
> +trap cleanup EXIT
> +
> +title_log "Check BTF type tag order."
> +
> +GCC=${GCC:-gcc}
> +CLANG=${CLANG:-clang}
> +PAHOLE=${PAHOLE:-pahole}
> +BPFTOOL=${BPFTOOL:-bpftool}
> +
> +if ! command -v "$BPFTOOL" > /dev/null; then
> +	info_log "skip: bpftool not available"
> +	test_skip
> +fi
> +
> +compiler_has_btf_type_tag()
> +{
> +	local compiler=$1
> +
> +	if ! command -v "$compiler" > /dev/null; then
> +		return 1
> +	fi
> +
> +	"$compiler" -x c -E -P - <<'EOF' 2>/dev/null | grep -qx 1
> +#ifndef __has_attribute
> +#define __has_attribute(x) 0
> +#endif
> +#if __has_attribute(btf_type_tag)
> +1
> +#else
> +0
> +#endif
> +EOF
> +}
> +
> +use_gcc=0
> +if compiler_has_btf_type_tag "$GCC"; then
> +	use_gcc=1
> +fi
> +
> +use_clang=0
> +if compiler_has_btf_type_tag "$CLANG"; then
> +	use_clang=1
> +fi
> +
> +if [ "$use_gcc" -eq 0 ] && [ "$use_clang" -eq 0 ]; then
> +	error_log "Need gcc or clang with btf_type_tag support for test $0"
> +	test_fail
> +fi
> +
> +src=$(cat <<EOF
> +#define __tag(x) __attribute__((btf_type_tag(#x)))
> +
> +struct sample {
> +	int value;
> +};
> +
> +struct sample __tag(outer) __tag(inner) *global_ptr;
> +
> +EOF
> +)
> +
> +check_type_tag_order()
> +{
> +	local btf=$1
> +	local dump
> +
> +	if ! dump=$("$BPFTOOL" btf dump file "$btf"); then
> +		return 1
> +	fi
> +
> +	printf '%s\n' "$dump" | awk '
> +	function parse_id(line, m) {
> +		if (match(line, /^\[([0-9]+)\]/, m))
> +			return m[1]
> +		return 0
> +	}
> +	function parse_name(line, m) {
> +		if (match(line, /\047([^\047]*)\047/, m))
> +			return m[1]
> +		return ""
> +	}
> +	function parse_type(line, m) {
> +		if (match(line, /type_id=([0-9]+)/, m))
> +			return m[1]
> +		return 0
> +	}
> +	function check_ptr(ptr, id, tags, seen) {
> +		id = type[ptr]
> +		while (id != 0 && !seen[id]) {
> +			seen[id] = 1
> +			if (kind[id] == "TYPE_TAG") {
> +				tags = tags (tags == "" ? "" : " -> ") name[id]
> +				id = type[id]
> +				continue
> +			}
> +			if (kind[id] == "STRUCT" && name[id] == "sample") {
> +				if (tags == "inner -> outer")
> +					exit 0
> +				candidates = candidates (candidates == "" ? "" : ", ") tags
> +			}
> +			return
> +		}
> +	}
> +	/^\[[0-9]+\]/ {
> +		id = parse_id($0)
> +		kind[id] = $2
> +		name[id] = parse_name($0)
> +		type[id] = parse_type($0)
> +		if (kind[id] == "PTR")
> +			ptrs[++nr_ptrs] = id
> +	}
> +	END {
> +		for (i = 1; i <= nr_ptrs; i++)
> +			check_ptr(ptrs[i])
> +		if (candidates != "") {
> +			print "type tag order mismatch; expected inner -> outer, found " candidates > "/dev/stderr"
> +			exit 1
> +		}
> +		print "could not find tagged pointer to struct sample" > "/dev/stderr"
> +		exit 1
> +	}'
> +}
> +
> +run_test()
> +{
> +	local compiler=$1
> +	local tmpobj=$2
> +	local btf=$3
> +
> +	info_log "Testing with $compiler"
> +
> +	if ! echo "$src" | "$compiler" -g -c -x c -o "$tmpobj" - 2>/dev/null; then
> +		error_log "Could not compile type tag order test with $compiler"
> +		return 1
> +	fi
> +
> +	if ! "$PAHOLE" --btf_features=+type_tag --btf_encode_detached="$btf" "$tmpobj" 2>/dev/null; then
> +		error_log "Could not encode BTF for $tmpobj"
> +		return 1
> +	fi
> +
> +	if check_type_tag_order "$btf"; then
> +		info_log "  passed"
> +		return 0
> +	fi
> +
> +	error_log "BTF type tag order does not match expected order ($compiler)"
> +	return 1
> +}
> +
> +failed=0
> +
> +if [ "$use_gcc" -eq 1 ]; then
> +	tmpobj=$(make_tmpobj)
> +	btf=${tmpobj%.o}.btf
> +	run_test "$GCC" "$tmpobj" "$btf" || failed=1
> +fi
> +
> +if [ "$use_clang" -eq 1 ]; then
> +	tmpobj=$(make_tmpobj)
> +	btf=${tmpobj%.o}.btf
> +	run_test "$CLANG" "$tmpobj" "$btf" || failed=1
> +fi
> +
> +if [ "$failed" -eq 0 ]; then
> +	test_pass
> +else
> +	test_fail
> +fi


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

end of thread, other threads:[~2026-06-18  4:47 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-18  0:57 [PAHOLE v5 0/5] support for DW_TAG_GNU_annotation and fixes Vineet Gupta
2026-06-18  0:57 ` [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters Vineet Gupta
2026-06-18  4:46   ` Emil Tsalapatis
2026-06-18  0:57 ` [PAHOLE v5 2/5] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta
2026-06-18  0:57 ` [PAHOLE v5 3/5] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
2026-06-18  0:57 ` [PAHOLE v5 4/5] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
2026-06-18  0:57 ` [PAHOLE v5 5/5] tests: Add btf_type_tag ordering test Vineet Gupta
2026-06-18  4:47   ` Emil Tsalapatis

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.