Dwarves debugging tools
 help / color / mirror / Atom feed
* [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC]
@ 2026-06-02 19:55 Vineet Gupta
  2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
                   ` (3 more replies)
  0 siblings, 4 replies; 15+ messages in thread
From: Vineet Gupta @ 2026-06-02 19:55 UTC (permalink / raw)
  To: dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust, 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>
---
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

[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] 15+ messages in thread

* [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-02 19:55 [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta
@ 2026-06-02 19:55 ` Vineet Gupta
  2026-06-03 20:08   ` Yonghong Song
                     ` (3 more replies)
  2026-06-02 19:55 ` [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
                   ` (2 subsequent siblings)
  3 siblings, 4 replies; 15+ messages in thread
From: Vineet Gupta @ 2026-06-02 19:55 UTC (permalink / raw)
  To: dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust, 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.

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.

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>
---
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)

[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    | 105 +++++++++++++++++++++++++++++++++++++++-------
 dwarves.h         |   2 +-
 dwarves_fprintf.c |  12 ++++--
 5 files changed, 110 insertions(+), 21 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..14f71c9043aa 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,40 @@ 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 for functions/struct/member tags.
+ * Pointers 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,6 +1625,8 @@ 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;
 }
@@ -1637,19 +1668,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 */
@@ -1662,6 +1695,26 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu,
 			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;
+
+		tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf);
+		if (tag == NULL)
+			return NULL;
+
+		die = &annot_die;
+	}
+
+out:
 	return tag ? &tag->tag : tag__new(die, cu);
 }
 
@@ -1690,6 +1743,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;
 }
 
@@ -2051,11 +2110,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 +2422,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 +2471,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 +2538,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 +2566,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 1ec478c2a027..757e4992a1f7 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] 15+ messages in thread

* [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test
  2026-06-02 19:55 [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta
  2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
@ 2026-06-02 19:55 ` Vineet Gupta
  2026-06-03 20:44   ` Emil Tsalapatis
  2026-06-03 21:52   ` Yonghong Song
  2026-06-03 20:18 ` [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Yonghong Song
  2026-06-03 20:37 ` Emil Tsalapatis
  3 siblings, 2 replies; 15+ messages in thread
From: Vineet Gupta @ 2026-06-02 19:55 UTC (permalink / raw)
  To: dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust, 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.

Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
 tests/pfunct-btf-decl-tags.sh | 72 +++++++++++++++++++++++++++--------
 1 file changed, 56 insertions(+), 16 deletions(-)

diff --git a/tests/pfunct-btf-decl-tags.sh b/tests/pfunct-btf-decl-tags.sh
index 35884b4e8687..a46aa1f3df55 100755
--- a/tests/pfunct-btf-decl-tags.sh
+++ b/tests/pfunct-btf-decl-tags.sh
@@ -6,24 +6,36 @@
 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) {}
@@ -31,7 +43,7 @@ __tag(a) __tag(b)          void bar(void) {}
 __tag(a)                   void buz(void) {}
 
 EOF
-) | $CLANG --target=bpf -c -g -x c -o $tmpobj -
+)
 
 # tags order is not guaranteed
 sort_tags=$(cat <<EOF
@@ -54,16 +66,44 @@ a void buz(void);
 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 --target=bpf -c -g -x c -o $tmpobj -
+	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] 15+ messages in thread

* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
@ 2026-06-03 20:08   ` Yonghong Song
  2026-06-03 20:54     ` Vineet Gupta
  2026-06-17 18:18     ` Vineet Gupta
  2026-06-03 20:42   ` Emil Tsalapatis
                     ` (2 subsequent siblings)
  3 siblings, 2 replies; 15+ messages in thread
From: Yonghong Song @ 2026-06-03 20:08 UTC (permalink / raw)
  To: Vineet Gupta, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust



On 6/2/26 12:55 PM, Vineet Gupta wrote:
> 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.
>
> 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.
>
> 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>
> ---
> 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)
>
> [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/

I download and build gcc-16.1 which will be used for gcc compilation.
I did some experiments with this patch set for both type tag and decl tag.
See below.

For type tag
============

$ cat type_tag.c
/* btf_type_tag test cases.
  *
  * btf_type_tag annotates a *type* and the tag becomes part of the type
  * chain in BTF (TYPE_TAG kind), e.g. used by the kernel for __user / __rcu
  * / __percpu style annotations.
  *
  * Placement matters: the tag is written *before* the '*' so it annotates
  * the pointee type.  This produces  PTR -> TYPE_TAG 'x' -> <pointee>.
  *
  * Build:  clang -O2 -g -target bpf -c type_tag.c -o type_tag.o
  *         /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c type_tag.c -o type_tag.o
  * Dump:   bpftool btf dump file type_tag.o
  */

#define __type_tag(x) __attribute__((btf_type_tag(x)))

/* a single type tag on a pointer's pointee */
int __type_tag("user") *user_ptr_var;

/* stacked type tags: the chain is preserved, outermost first */
int __type_tag("tag1") __type_tag("tag2") *stacked_var;

/* type tags inside struct members */
struct bar {
         char __type_tag("rcu") *name;
         int  __type_tag("percpu") *counter;
};

/* type tag on a function parameter's pointee */
int read_user(int __type_tag("user") *p)
{
         return *p;
}

/* keep things referenced */
int use(struct bar *b)
{
         return read_user(user_ptr_var) + (stacked_var ? *stacked_var : 0) +
                (b->counter ? *b->counter : 0);
}

$ /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c type_tag.c -o type_tag.o
$ bpftool btf dump file type_tag.o
[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
[2] PTR '(anon)' type_id=15
[3] PTR '(anon)' type_id=17
[4] STRUCT 'bar' size=16 vlen=2
         'name' type_id=6 bits_offset=0
         'counter' type_id=7 bits_offset=64
[5] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED
[6] PTR '(anon)' type_id=18
[7] PTR '(anon)' type_id=19
[8] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1
         'b' type_id=9
[9] PTR '(anon)' type_id=4
[10] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1
         'p' type_id=2
[11] VAR 'stacked_var' type_id=3, linkage=global
[12] VAR 'user_ptr_var' type_id=2, linkage=global
[13] FUNC 'use' type_id=8 linkage=global
[14] FUNC 'read_user' type_id=10 linkage=global
[15] TYPE_TAG 'user' type_id=1
[16] TYPE_TAG 'tag1' type_id=1
[17] TYPE_TAG 'tag2' type_id=16
[18] TYPE_TAG 'rcu' type_id=5
[19] TYPE_TAG 'percpu' type_id=1
[20] DATASEC '.bss' size=0 vlen=2
         type_id=11 offset=0 size=8 (VAR 'stacked_var')
         type_id=12 offset=0 size=8 (VAR 'user_ptr_var')
$

$ clang -O2 -g -target bpf -c type_tag.c -o type_tag.o
$ bpftool btf dump file type_tag.o
[1] TYPE_TAG 'user' type_id=3
[2] PTR '(anon)' type_id=1
[3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
[4] FUNC_PROTO '(anon)' ret_type_id=3 vlen=1
         'p' type_id=2
[5] FUNC 'read_user' type_id=4 linkage=global
[6] PTR '(anon)' type_id=7
[7] STRUCT 'bar' size=16 vlen=2
         'name' type_id=9 bits_offset=0
         'counter' type_id=12 bits_offset=64
[8] TYPE_TAG 'rcu' type_id=10
[9] PTR '(anon)' type_id=8
[10] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED
[11] TYPE_TAG 'percpu' type_id=3
[12] PTR '(anon)' type_id=11
[13] FUNC_PROTO '(anon)' ret_type_id=3 vlen=1
         'b' type_id=6
[14] FUNC 'use' type_id=13 linkage=global
[15] VAR 'user_ptr_var' type_id=2, linkage=global
[16] TYPE_TAG 'tag1' type_id=3
[17] TYPE_TAG 'tag2' type_id=16
[18] PTR '(anon)' type_id=17
[19] VAR 'stacked_var' type_id=18, linkage=global
[20] DATASEC '.bss' size=0 vlen=2
         type_id=15 offset=0 size=8 (VAR 'user_ptr_var')
         type_id=19 offset=0 size=8 (VAR 'stacked_var')
$

So type tag matches between clang and gcc.

For decl tag
============

$ cat decl_tag.c
/* btf_decl_tag test cases.
  *
  * btf_decl_tag can be attached to:
  *   - global (incl. static) variables
  *   - functions
  *   - function parameters
  *   - struct/union types and their members
  *   - typedefs
  *
  * Build:  clang -O2 -target bpf -g -c decl_tag.c -o decl_tag.o
  *         /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c decl_tag.c -o decl_tag.o
  * Dump:   bpftool btf dump file decl_tag.o
  */

#define __tag(x) __attribute__((btf_decl_tag(x)))

/* tag on a global variable */
int global_var __tag("global_var_tag");

/* tag on a static variable */
static int static_var __tag("static_var_tag");

/* multiple tags on one declaration */
int multi_tag_var __tag("tag_a") __tag("tag_b");

/* tag on struct type and its members */
struct foo {
         int a __tag("member_a_tag");
         int b __tag("member_b_tag");
} __tag("struct_foo_tag");

/* tag on a typedef */
typedef struct foo foo_t1 __tag("typedef_foo_tag");
typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag");

/* tag on a function and its parameters */
__tag("func_add_tag")
int add(int x __tag("param_x_tag"), int y __tag("param_y_tag"))
{
         return x + y;
}

/* keep the globals/types alive so they land in BTF */
int use(foo_t1 *f, foo_t2 *g)
{
         return add(global_var + static_var + multi_tag_var, f->a + g->foo2);
}

$ /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c decl_tag.c -o decl_tag.o
decl_tag.c:30:1: warning: ‘btf_decl_tag’ attribute does not apply to types [-Wattributes]
    30 | } __tag("struct_foo_tag");
       | ^

$ bpftool btf dump file decl_tag.o
[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
[2] STRUCT 'foo' size=8 vlen=2
         'a' type_id=1 bits_offset=0
         'b' type_id=1 bits_offset=32
[3] TYPEDEF 'foo_t1' type_id=2
[4] STRUCT '(anon)' size=4 vlen=1
         'foo2' type_id=1 bits_offset=0
[5] TYPEDEF 'foo_t2' type_id=4
[6] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2
         'f' type_id=7
         'g' type_id=8
[7] PTR '(anon)' type_id=3
[8] PTR '(anon)' type_id=5
[9] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2
         'x' type_id=1
         'y' type_id=1
[10] VAR 'multi_tag_var' type_id=1, linkage=global
[11] VAR 'global_var' type_id=1, linkage=global
[12] FUNC 'use' type_id=6 linkage=global
[13] FUNC 'add' type_id=9 linkage=global
[14] DECL_TAG 'global_var_tag' type_id=11 component_idx=-1
[15] DECL_TAG 'static_var_tag' type_id=0 component_idx=-1
[16] DECL_TAG 'tag_b' type_id=10 component_idx=-1
[17] DECL_TAG 'tag_a' type_id=10 component_idx=-1
[18] DECL_TAG 'member_a_tag' type_id=2 component_idx=0
[19] DECL_TAG 'member_b_tag' type_id=2 component_idx=1
[20] DECL_TAG 'param_x_tag' type_id=13 component_idx=0
[21] DECL_TAG 'param_y_tag' type_id=13 component_idx=1
[22] DECL_TAG 'func_add_tag' type_id=13 component_idx=-1
[23] DATASEC '.bss' size=0 vlen=2
         type_id=10 offset=0 size=4 (VAR 'multi_tag_var')
         type_id=11 offset=0 size=4 (VAR 'global_var')
$ bpftool btf dump file decl_tag.o | grep DECL_TAG
[14] DECL_TAG 'global_var_tag' type_id=11 component_idx=-1
[15] DECL_TAG 'static_var_tag' type_id=0 component_idx=-1
[16] DECL_TAG 'tag_b' type_id=10 component_idx=-1
[17] DECL_TAG 'tag_a' type_id=10 component_idx=-1
[18] DECL_TAG 'member_a_tag' type_id=2 component_idx=0
[19] DECL_TAG 'member_b_tag' type_id=2 component_idx=1
[20] DECL_TAG 'param_x_tag' type_id=13 component_idx=0
[21] DECL_TAG 'param_y_tag' type_id=13 component_idx=1
[22] DECL_TAG 'func_add_tag' type_id=13 component_idx=-1
$

Three decl tags (struct_foo_tag, typedef_foo_tag and typedef_foo2_tag)
are missing here:

struct foo {
         int a __tag("member_a_tag");
         int b __tag("member_b_tag");
} __tag("struct_foo_tag");

/* tag on a typedef */
typedef struct foo foo_t1 __tag("typedef_foo_tag");
typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag");

$ clang -O2 -g -target bpf -c decl_tag.c -o decl_tag.o
$ bpftool btf dump file decl_tag.o
[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2
         'x' type_id=1
         'y' type_id=1
[3] FUNC 'add' type_id=2 linkage=global
[4] DECL_TAG 'param_x_tag' type_id=3 component_idx=0
[5] DECL_TAG 'param_y_tag' type_id=3 component_idx=1
[6] DECL_TAG 'func_add_tag' type_id=3 component_idx=-1
[7] PTR '(anon)' type_id=8
[8] TYPEDEF 'foo_t1' type_id=10
[9] DECL_TAG 'typedef_foo_tag' type_id=8 component_idx=-1
[10] STRUCT 'foo' size=8 vlen=2
         'a' type_id=1 bits_offset=0
         'b' type_id=1 bits_offset=32
[11] DECL_TAG 'struct_foo_tag' type_id=10 component_idx=-1
[12] DECL_TAG 'member_a_tag' type_id=10 component_idx=0
[13] DECL_TAG 'member_b_tag' type_id=10 component_idx=1
[14] PTR '(anon)' type_id=15
[15] TYPEDEF 'foo_t2' type_id=17
[16] DECL_TAG 'typedef_foo2_tag' type_id=15 component_idx=-1
[17] STRUCT '(anon)' size=4 vlen=1
         'foo2' type_id=1 bits_offset=0
[18] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2
         'f' type_id=7
         'g' type_id=14
[19] FUNC 'use' type_id=18 linkage=global
[20] VAR 'global_var' type_id=1, linkage=global
[21] DECL_TAG 'global_var_tag' type_id=20 component_idx=-1
[22] VAR 'multi_tag_var' type_id=1, linkage=global
[23] DECL_TAG 'tag_a' type_id=22 component_idx=-1
[24] DECL_TAG 'tag_b' type_id=22 component_idx=-1
[25] DATASEC '.bss' size=0 vlen=2
         type_id=20 offset=0 size=4 (VAR 'global_var')
         type_id=22 offset=0 size=4 (VAR 'multi_tag_var')
$ bpftool btf dump file decl_tag.o | grep DECL_TAG
[4] DECL_TAG 'param_x_tag' type_id=3 component_idx=0
[5] DECL_TAG 'param_y_tag' type_id=3 component_idx=1
[6] DECL_TAG 'func_add_tag' type_id=3 component_idx=-1
[9] DECL_TAG 'typedef_foo_tag' type_id=8 component_idx=-1
[11] DECL_TAG 'struct_foo_tag' type_id=10 component_idx=-1
[12] DECL_TAG 'member_a_tag' type_id=10 component_idx=0
[13] DECL_TAG 'member_b_tag' type_id=10 component_idx=1
[16] DECL_TAG 'typedef_foo2_tag' type_id=15 component_idx=-1
[21] DECL_TAG 'global_var_tag' type_id=20 component_idx=-1
[23] DECL_TAG 'tag_a' type_id=22 component_idx=-1
[24] DECL_TAG 'tag_b' type_id=22 component_idx=-1

DECL_TAG 'static_var_tag' type_id=0 component_idx=-1 is missing in llvm.
This is execpted for llvm since 'static_var' is 'inlined' since it is
value is 0 and the compiler optimization removed it in function use().
In llvm Dwarf->BTF conversion is very late and we only emit survived
globals.

gcc emits 'static_var_tag' probably in frontend. Emitting
'static_var_tag' is okay, just not used.

I think gcc should support
  - declaration tag for typedef, and
  - declaration tag for the whole struct (like above 'struct_foo_tag')
to be compatible with llvm.

> ---
>   btf_encoder.c     |   1 +
>   dutil.h           |  11 +++++
>   dwarf_loader.c    | 105 +++++++++++++++++++++++++++++++++++++++-------
>   dwarves.h         |   2 +-
>   dwarves_fprintf.c |  12 ++++--
>   5 files changed, 110 insertions(+), 21 deletions(-)

[...]


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

* Re: [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC]
  2026-06-02 19:55 [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta
  2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
  2026-06-02 19:55 ` [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
@ 2026-06-03 20:18 ` Yonghong Song
  2026-06-03 20:37 ` Emil Tsalapatis
  3 siblings, 0 replies; 15+ messages in thread
From: Yonghong Song @ 2026-06-03 20:18 UTC (permalink / raw)
  To: Vineet Gupta, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust



On 6/2/26 12:55 PM, Vineet Gupta wrote:
> 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>

> ---
> 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
>
> [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/

If there is a further revision, for a three patch series, I think it is worthwhile
to have a cover letter so the above change log (and 2nd patch or more)
can be all in cover letter.

> ---
>   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;
>   }
>   
>
[...]


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

* Re: [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC]
  2026-06-02 19:55 [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta
                   ` (2 preceding siblings ...)
  2026-06-03 20:18 ` [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Yonghong Song
@ 2026-06-03 20:37 ` Emil Tsalapatis
  3 siblings, 0 replies; 15+ messages in thread
From: Emil Tsalapatis @ 2026-06-03 20:37 UTC (permalink / raw)
  To: Vineet Gupta, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust

On Tue Jun 2, 2026 at 3:55 PM EDT, Vineet Gupta wrote:
> 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>

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

> ---
> 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
>
> [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);


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

* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
  2026-06-03 20:08   ` Yonghong Song
@ 2026-06-03 20:42   ` Emil Tsalapatis
  2026-06-03 21:41   ` Yonghong Song
  2026-06-07  9:54   ` Alan Maguire
  3 siblings, 0 replies; 15+ messages in thread
From: Emil Tsalapatis @ 2026-06-03 20:42 UTC (permalink / raw)
  To: Vineet Gupta, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust

On Tue Jun 2, 2026 at 3:55 PM EDT, Vineet Gupta wrote:
> 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.
>
> 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.
>
> 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>

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

One thing, we accept for both LLVM and GCC annotation tags being used as
LLVM annotation tags because we check against tags__is_annotation().
This is valid imo.

> ---
> 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)
>
> [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    | 105 +++++++++++++++++++++++++++++++++++++++-------
>  dwarves.h         |   2 +-
>  dwarves_fprintf.c |  12 ++++--
>  5 files changed, 110 insertions(+), 21 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..14f71c9043aa 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,40 @@ 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 for functions/struct/member tags.
> + * Pointers 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,6 +1625,8 @@ 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;
>  }
> @@ -1637,19 +1668,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 */
> @@ -1662,6 +1695,26 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu,
>  			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;
> +
> +		tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf);
> +		if (tag == NULL)
> +			return NULL;
> +
> +		die = &annot_die;
> +	}
> +
> +out:
>  	return tag ? &tag->tag : tag__new(die, cu);
>  }
>  
> @@ -1690,6 +1743,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;
>  }
>  
> @@ -2051,11 +2110,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 +2422,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 +2471,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 +2538,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 +2566,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 1ec478c2a027..757e4992a1f7 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;


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

* Re: [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test
  2026-06-02 19:55 ` [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
@ 2026-06-03 20:44   ` Emil Tsalapatis
  2026-06-03 21:52   ` Yonghong Song
  1 sibling, 0 replies; 15+ messages in thread
From: Emil Tsalapatis @ 2026-06-03 20:44 UTC (permalink / raw)
  To: Vineet Gupta, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust

On Tue Jun 2, 2026 at 3:55 PM EDT, Vineet Gupta wrote:
> 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.
>
> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>

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

> ---
>  tests/pfunct-btf-decl-tags.sh | 72 +++++++++++++++++++++++++++--------
>  1 file changed, 56 insertions(+), 16 deletions(-)
>
> diff --git a/tests/pfunct-btf-decl-tags.sh b/tests/pfunct-btf-decl-tags.sh
> index 35884b4e8687..a46aa1f3df55 100755
> --- a/tests/pfunct-btf-decl-tags.sh
> +++ b/tests/pfunct-btf-decl-tags.sh
> @@ -6,24 +6,36 @@
>  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) {}
> @@ -31,7 +43,7 @@ __tag(a) __tag(b)          void bar(void) {}
>  __tag(a)                   void buz(void) {}
>  
>  EOF
> -) | $CLANG --target=bpf -c -g -x c -o $tmpobj -
> +)
>  
>  # tags order is not guaranteed
>  sort_tags=$(cat <<EOF
> @@ -54,16 +66,44 @@ a void buz(void);
>  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 --target=bpf -c -g -x c -o $tmpobj -
> +	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


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

* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-03 20:08   ` Yonghong Song
@ 2026-06-03 20:54     ` Vineet Gupta
  2026-06-03 21:40       ` Yonghong Song
  2026-06-17 18:18     ` Vineet Gupta
  1 sibling, 1 reply; 15+ messages in thread
From: Vineet Gupta @ 2026-06-03 20:54 UTC (permalink / raw)
  To: Yonghong Song, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust

Hi Yonghong,

Thanks for reviewing the patches.

On 6/3/26 1:08 PM, Yonghong Song wrote:
>> [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/
> I download and build gcc-16.1 which will be used for gcc compilation.
> I did some experiments with this patch set for both type tag and decl tag.
> See below.

FWIW, you definitely need a fix [1] on top of gcc 16.1.
The patch explains the reasoning and did cause some missing entries in 
type chain.
I haven't checked if this changes anything specific to issues you point 
below.

[1] https://gcc.gnu.org/pipermail/bpf/2026-May/000147.html


>
> For type tag
> ============
>
> $ cat type_tag.c
> /* btf_type_tag test cases.
>    *
>    * btf_type_tag annotates a *type* and the tag becomes part of the type
>    * chain in BTF (TYPE_TAG kind), e.g. used by the kernel for __user / __rcu
>    * / __percpu style annotations.
>    *
>    * Placement matters: the tag is written *before* the '*' so it annotates
>    * the pointee type.  This produces  PTR -> TYPE_TAG 'x' -> <pointee>.
>    *
>    * Build:  clang -O2 -g -target bpf -c type_tag.c -o type_tag.o
>    *         /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c type_tag.c -o type_tag.o
>    * Dump:   bpftool btf dump file type_tag.o
>    */
>
> #define __type_tag(x) __attribute__((btf_type_tag(x)))
>
> /* a single type tag on a pointer's pointee */
> int __type_tag("user") *user_ptr_var;
>
> /* stacked type tags: the chain is preserved, outermost first */
> int __type_tag("tag1") __type_tag("tag2") *stacked_var;
>
> /* type tags inside struct members */
> struct bar {
>           char __type_tag("rcu") *name;
>           int  __type_tag("percpu") *counter;
> };
>
> /* type tag on a function parameter's pointee */
> int read_user(int __type_tag("user") *p)
> {
>           return *p;
> }
>
> /* keep things referenced */
> int use(struct bar *b)
> {
>           return read_user(user_ptr_var) + (stacked_var ? *stacked_var : 0) +
>                  (b->counter ? *b->counter : 0);
> }
>
> $ /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c type_tag.c -o type_tag.o
> $ bpftool btf dump file type_tag.o
> [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
> [2] PTR '(anon)' type_id=15
> [3] PTR '(anon)' type_id=17
> [4] STRUCT 'bar' size=16 vlen=2
>           'name' type_id=6 bits_offset=0
>           'counter' type_id=7 bits_offset=64
> [5] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED
> [6] PTR '(anon)' type_id=18
> [7] PTR '(anon)' type_id=19
> [8] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1
>           'b' type_id=9
> [9] PTR '(anon)' type_id=4
> [10] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1
>           'p' type_id=2
> [11] VAR 'stacked_var' type_id=3, linkage=global
> [12] VAR 'user_ptr_var' type_id=2, linkage=global
> [13] FUNC 'use' type_id=8 linkage=global
> [14] FUNC 'read_user' type_id=10 linkage=global
> [15] TYPE_TAG 'user' type_id=1
> [16] TYPE_TAG 'tag1' type_id=1
> [17] TYPE_TAG 'tag2' type_id=16
> [18] TYPE_TAG 'rcu' type_id=5
> [19] TYPE_TAG 'percpu' type_id=1
> [20] DATASEC '.bss' size=0 vlen=2
>           type_id=11 offset=0 size=8 (VAR 'stacked_var')
>           type_id=12 offset=0 size=8 (VAR 'user_ptr_var')
> $
>
> $ clang -O2 -g -target bpf -c type_tag.c -o type_tag.o
> $ bpftool btf dump file type_tag.o
> [1] TYPE_TAG 'user' type_id=3
> [2] PTR '(anon)' type_id=1
> [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
> [4] FUNC_PROTO '(anon)' ret_type_id=3 vlen=1
>           'p' type_id=2
> [5] FUNC 'read_user' type_id=4 linkage=global
> [6] PTR '(anon)' type_id=7
> [7] STRUCT 'bar' size=16 vlen=2
>           'name' type_id=9 bits_offset=0
>           'counter' type_id=12 bits_offset=64
> [8] TYPE_TAG 'rcu' type_id=10
> [9] PTR '(anon)' type_id=8
> [10] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED
> [11] TYPE_TAG 'percpu' type_id=3
> [12] PTR '(anon)' type_id=11
> [13] FUNC_PROTO '(anon)' ret_type_id=3 vlen=1
>           'b' type_id=6
> [14] FUNC 'use' type_id=13 linkage=global
> [15] VAR 'user_ptr_var' type_id=2, linkage=global
> [16] TYPE_TAG 'tag1' type_id=3
> [17] TYPE_TAG 'tag2' type_id=16
> [18] PTR '(anon)' type_id=17
> [19] VAR 'stacked_var' type_id=18, linkage=global
> [20] DATASEC '.bss' size=0 vlen=2
>           type_id=15 offset=0 size=8 (VAR 'user_ptr_var')
>           type_id=19 offset=0 size=8 (VAR 'stacked_var')
> $
>
> So type tag matches between clang and gcc.

Nice, thanks for cross-checking and confirming.


> For decl tag
> ============
>
> $ cat decl_tag.c
> /* btf_decl_tag test cases.
>    *
>    * btf_decl_tag can be attached to:
>    *   - global (incl. static) variables
>    *   - functions
>    *   - function parameters
>    *   - struct/union types and their members
>    *   - typedefs
>    *
>    * Build:  clang -O2 -target bpf -g -c decl_tag.c -o decl_tag.o
>    *         /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c decl_tag.c -o decl_tag.o
>    * Dump:   bpftool btf dump file decl_tag.o
>    */
>
> #define __tag(x) __attribute__((btf_decl_tag(x)))
>
> /* tag on a global variable */
> int global_var __tag("global_var_tag");
>
> /* tag on a static variable */
> static int static_var __tag("static_var_tag");
>
> /* multiple tags on one declaration */
> int multi_tag_var __tag("tag_a") __tag("tag_b");
>
> /* tag on struct type and its members */
> struct foo {
>           int a __tag("member_a_tag");
>           int b __tag("member_b_tag");
> } __tag("struct_foo_tag");
>
> /* tag on a typedef */
> typedef struct foo foo_t1 __tag("typedef_foo_tag");
> typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag");
>
> /* tag on a function and its parameters */
> __tag("func_add_tag")
> int add(int x __tag("param_x_tag"), int y __tag("param_y_tag"))
> {
>           return x + y;
> }
>
> /* keep the globals/types alive so they land in BTF */
> int use(foo_t1 *f, foo_t2 *g)
> {
>           return add(global_var + static_var + multi_tag_var, f->a + g->foo2);
> }
>
> $ /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c decl_tag.c -o decl_tag.o
> decl_tag.c:30:1: warning: ‘btf_decl_tag’ attribute does not apply to types [-Wattributes]
>      30 | } __tag("struct_foo_tag");
>         | ^
>
> $ bpftool btf dump file decl_tag.o
> [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
> [2] STRUCT 'foo' size=8 vlen=2
>           'a' type_id=1 bits_offset=0
>           'b' type_id=1 bits_offset=32
> [3] TYPEDEF 'foo_t1' type_id=2
> [4] STRUCT '(anon)' size=4 vlen=1
>           'foo2' type_id=1 bits_offset=0
> [5] TYPEDEF 'foo_t2' type_id=4
> [6] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2
>           'f' type_id=7
>           'g' type_id=8
> [7] PTR '(anon)' type_id=3
> [8] PTR '(anon)' type_id=5
> [9] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2
>           'x' type_id=1
>           'y' type_id=1
> [10] VAR 'multi_tag_var' type_id=1, linkage=global
> [11] VAR 'global_var' type_id=1, linkage=global
> [12] FUNC 'use' type_id=6 linkage=global
> [13] FUNC 'add' type_id=9 linkage=global
> [14] DECL_TAG 'global_var_tag' type_id=11 component_idx=-1
> [15] DECL_TAG 'static_var_tag' type_id=0 component_idx=-1
> [16] DECL_TAG 'tag_b' type_id=10 component_idx=-1
> [17] DECL_TAG 'tag_a' type_id=10 component_idx=-1
> [18] DECL_TAG 'member_a_tag' type_id=2 component_idx=0
> [19] DECL_TAG 'member_b_tag' type_id=2 component_idx=1
> [20] DECL_TAG 'param_x_tag' type_id=13 component_idx=0
> [21] DECL_TAG 'param_y_tag' type_id=13 component_idx=1
> [22] DECL_TAG 'func_add_tag' type_id=13 component_idx=-1
> [23] DATASEC '.bss' size=0 vlen=2
>           type_id=10 offset=0 size=4 (VAR 'multi_tag_var')
>           type_id=11 offset=0 size=4 (VAR 'global_var')
> $ bpftool btf dump file decl_tag.o | grep DECL_TAG
> [14] DECL_TAG 'global_var_tag' type_id=11 component_idx=-1
> [15] DECL_TAG 'static_var_tag' type_id=0 component_idx=-1
> [16] DECL_TAG 'tag_b' type_id=10 component_idx=-1
> [17] DECL_TAG 'tag_a' type_id=10 component_idx=-1
> [18] DECL_TAG 'member_a_tag' type_id=2 component_idx=0
> [19] DECL_TAG 'member_b_tag' type_id=2 component_idx=1
> [20] DECL_TAG 'param_x_tag' type_id=13 component_idx=0
> [21] DECL_TAG 'param_y_tag' type_id=13 component_idx=1
> [22] DECL_TAG 'func_add_tag' type_id=13 component_idx=-1
> $
>
> Three decl tags (struct_foo_tag, typedef_foo_tag and typedef_foo2_tag)
> are missing here:

I'll double check, once I'm back from PTO.

> struct foo {
>           int a __tag("member_a_tag");
>           int b __tag("member_b_tag");
> } __tag("struct_foo_tag");
>
> /* tag on a typedef */
> typedef struct foo foo_t1 __tag("typedef_foo_tag");
> typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag");
>
> $ clang -O2 -g -target bpf -c decl_tag.c -o decl_tag.o
> $ bpftool btf dump file decl_tag.o
> [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
> [2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2
>           'x' type_id=1
>           'y' type_id=1
> [3] FUNC 'add' type_id=2 linkage=global
> [4] DECL_TAG 'param_x_tag' type_id=3 component_idx=0
> [5] DECL_TAG 'param_y_tag' type_id=3 component_idx=1
> [6] DECL_TAG 'func_add_tag' type_id=3 component_idx=-1
> [7] PTR '(anon)' type_id=8
> [8] TYPEDEF 'foo_t1' type_id=10
> [9] DECL_TAG 'typedef_foo_tag' type_id=8 component_idx=-1
> [10] STRUCT 'foo' size=8 vlen=2
>           'a' type_id=1 bits_offset=0
>           'b' type_id=1 bits_offset=32
> [11] DECL_TAG 'struct_foo_tag' type_id=10 component_idx=-1
> [12] DECL_TAG 'member_a_tag' type_id=10 component_idx=0
> [13] DECL_TAG 'member_b_tag' type_id=10 component_idx=1
> [14] PTR '(anon)' type_id=15
> [15] TYPEDEF 'foo_t2' type_id=17
> [16] DECL_TAG 'typedef_foo2_tag' type_id=15 component_idx=-1
> [17] STRUCT '(anon)' size=4 vlen=1
>           'foo2' type_id=1 bits_offset=0
> [18] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2
>           'f' type_id=7
>           'g' type_id=14
> [19] FUNC 'use' type_id=18 linkage=global
> [20] VAR 'global_var' type_id=1, linkage=global
> [21] DECL_TAG 'global_var_tag' type_id=20 component_idx=-1
> [22] VAR 'multi_tag_var' type_id=1, linkage=global
> [23] DECL_TAG 'tag_a' type_id=22 component_idx=-1
> [24] DECL_TAG 'tag_b' type_id=22 component_idx=-1
> [25] DATASEC '.bss' size=0 vlen=2
>           type_id=20 offset=0 size=4 (VAR 'global_var')
>           type_id=22 offset=0 size=4 (VAR 'multi_tag_var')
> $ bpftool btf dump file decl_tag.o | grep DECL_TAG
> [4] DECL_TAG 'param_x_tag' type_id=3 component_idx=0
> [5] DECL_TAG 'param_y_tag' type_id=3 component_idx=1
> [6] DECL_TAG 'func_add_tag' type_id=3 component_idx=-1
> [9] DECL_TAG 'typedef_foo_tag' type_id=8 component_idx=-1
> [11] DECL_TAG 'struct_foo_tag' type_id=10 component_idx=-1
> [12] DECL_TAG 'member_a_tag' type_id=10 component_idx=0
> [13] DECL_TAG 'member_b_tag' type_id=10 component_idx=1
> [16] DECL_TAG 'typedef_foo2_tag' type_id=15 component_idx=-1
> [21] DECL_TAG 'global_var_tag' type_id=20 component_idx=-1
> [23] DECL_TAG 'tag_a' type_id=22 component_idx=-1
> [24] DECL_TAG 'tag_b' type_id=22 component_idx=-1
>
> DECL_TAG 'static_var_tag' type_id=0 component_idx=-1 is missing in llvm.
> This is execpted for llvm since 'static_var' is 'inlined' since it is
> value is 0 and the compiler optimization removed it in function use().
> In llvm Dwarf->BTF conversion is very late and we only emit survived
> globals.
>
> gcc emits 'static_var_tag' probably in frontend. Emitting
> 'static_var_tag' is okay, just not used.
>
> I think gcc should support
>    - declaration tag for typedef, and
>    - declaration tag for the whole struct (like above 'struct_foo_tag')
> to be compatible with llvm.

OK we will look into this.

Thx,
-Vineet


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

* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-03 20:54     ` Vineet Gupta
@ 2026-06-03 21:40       ` Yonghong Song
  0 siblings, 0 replies; 15+ messages in thread
From: Yonghong Song @ 2026-06-03 21:40 UTC (permalink / raw)
  To: Vineet Gupta, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust



On 6/3/26 1:54 PM, Vineet Gupta wrote:
> Hi Yonghong,
>
> Thanks for reviewing the patches.
>
> On 6/3/26 1:08 PM, Yonghong Song wrote:
>>> [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/
>> I download and build gcc-16.1 which will be used for gcc compilation.
>> I did some experiments with this patch set for both type tag and decl 
>> tag.
>> See below.
>
> FWIW, you definitely need a fix [1] on top of gcc 16.1.
> The patch explains the reasoning and did cause some missing entries in 
> type chain.
> I haven't checked if this changes anything specific to issues you 
> point below.
>
> [1] https://gcc.gnu.org/pipermail/bpf/2026-May/000147.html

Thanks for the pointer. The fix is related to type tag. So I suspect
typedef and struct decl_tag support is not done yet in gcc.

>
>
>>
>> For type tag
>> ============
>>
>> $ cat type_tag.c
>> /* btf_type_tag test cases.
>>    *
>>    * btf_type_tag annotates a *type* and the tag becomes part of the 
>> type
>>    * chain in BTF (TYPE_TAG kind), e.g. used by the kernel for __user 
>> / __rcu
>>    * / __percpu style annotations.
>>    *
>>    * Placement matters: the tag is written *before* the '*' so it 
>> annotates
>>    * the pointee type.  This produces  PTR -> TYPE_TAG 'x' -> <pointee>.
>>    *
>>    * Build:  clang -O2 -g -target bpf -c type_tag.c -o type_tag.o
>>    *         /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf 
>> -g -c type_tag.c -o type_tag.o
>>    * Dump:   bpftool btf dump file type_tag.o
>>    */
>>
>> #define __type_tag(x) __attribute__((btf_type_tag(x)))
>>
>> /* a single type tag on a pointer's pointee */
>> int __type_tag("user") *user_ptr_var;
>>
>> /* stacked type tags: the chain is preserved, outermost first */
>> int __type_tag("tag1") __type_tag("tag2") *stacked_var;
>>
>> /* type tags inside struct members */
>> struct bar {
>>           char __type_tag("rcu") *name;
>>           int  __type_tag("percpu") *counter;
>> };
>>
>> /* type tag on a function parameter's pointee */
>> int read_user(int __type_tag("user") *p)
>> {
>>           return *p;
>> }
>>
>> /* keep things referenced */
>> int use(struct bar *b)
>> {
>>           return read_user(user_ptr_var) + (stacked_var ? 
>> *stacked_var : 0) +
>>                  (b->counter ? *b->counter : 0);
>> }
>>
>> $ /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c 
>> type_tag.c -o type_tag.o
>> $ bpftool btf dump file type_tag.o
>> [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
>> [2] PTR '(anon)' type_id=15
>> [3] PTR '(anon)' type_id=17
>> [4] STRUCT 'bar' size=16 vlen=2
>>           'name' type_id=6 bits_offset=0
>>           'counter' type_id=7 bits_offset=64
>> [5] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED
>> [6] PTR '(anon)' type_id=18
>> [7] PTR '(anon)' type_id=19
>> [8] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1
>>           'b' type_id=9
>> [9] PTR '(anon)' type_id=4
>> [10] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1
>>           'p' type_id=2
>> [11] VAR 'stacked_var' type_id=3, linkage=global
>> [12] VAR 'user_ptr_var' type_id=2, linkage=global
>> [13] FUNC 'use' type_id=8 linkage=global
>> [14] FUNC 'read_user' type_id=10 linkage=global
>> [15] TYPE_TAG 'user' type_id=1
>> [16] TYPE_TAG 'tag1' type_id=1
>> [17] TYPE_TAG 'tag2' type_id=16
>> [18] TYPE_TAG 'rcu' type_id=5
>> [19] TYPE_TAG 'percpu' type_id=1
>> [20] DATASEC '.bss' size=0 vlen=2
>>           type_id=11 offset=0 size=8 (VAR 'stacked_var')
>>           type_id=12 offset=0 size=8 (VAR 'user_ptr_var')
>> $
>>
>> $ clang -O2 -g -target bpf -c type_tag.c -o type_tag.o
>> $ bpftool btf dump file type_tag.o
>> [1] TYPE_TAG 'user' type_id=3
>> [2] PTR '(anon)' type_id=1
>> [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
>> [4] FUNC_PROTO '(anon)' ret_type_id=3 vlen=1
>>           'p' type_id=2
>> [5] FUNC 'read_user' type_id=4 linkage=global
>> [6] PTR '(anon)' type_id=7
>> [7] STRUCT 'bar' size=16 vlen=2
>>           'name' type_id=9 bits_offset=0
>>           'counter' type_id=12 bits_offset=64
>> [8] TYPE_TAG 'rcu' type_id=10
>> [9] PTR '(anon)' type_id=8
>> [10] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED
>> [11] TYPE_TAG 'percpu' type_id=3
>> [12] PTR '(anon)' type_id=11
>> [13] FUNC_PROTO '(anon)' ret_type_id=3 vlen=1
>>           'b' type_id=6
>> [14] FUNC 'use' type_id=13 linkage=global
>> [15] VAR 'user_ptr_var' type_id=2, linkage=global
>> [16] TYPE_TAG 'tag1' type_id=3
>> [17] TYPE_TAG 'tag2' type_id=16
>> [18] PTR '(anon)' type_id=17
>> [19] VAR 'stacked_var' type_id=18, linkage=global
>> [20] DATASEC '.bss' size=0 vlen=2
>>           type_id=15 offset=0 size=8 (VAR 'user_ptr_var')
>>           type_id=19 offset=0 size=8 (VAR 'stacked_var')
>> $
>>
>> So type tag matches between clang and gcc.
>
> Nice, thanks for cross-checking and confirming.
>
>
>> For decl tag
>> ============
>>
>> $ cat decl_tag.c
>> /* btf_decl_tag test cases.
>>    *
>>    * btf_decl_tag can be attached to:
>>    *   - global (incl. static) variables
>>    *   - functions
>>    *   - function parameters
>>    *   - struct/union types and their members
>>    *   - typedefs
>>    *
>>    * Build:  clang -O2 -target bpf -g -c decl_tag.c -o decl_tag.o
>>    *         /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf 
>> -g -c decl_tag.c -o decl_tag.o
>>    * Dump:   bpftool btf dump file decl_tag.o
>>    */
>>
>> #define __tag(x) __attribute__((btf_decl_tag(x)))
>>
>> /* tag on a global variable */
>> int global_var __tag("global_var_tag");
>>
>> /* tag on a static variable */
>> static int static_var __tag("static_var_tag");
>>
>> /* multiple tags on one declaration */
>> int multi_tag_var __tag("tag_a") __tag("tag_b");
>>
>> /* tag on struct type and its members */
>> struct foo {
>>           int a __tag("member_a_tag");
>>           int b __tag("member_b_tag");
>> } __tag("struct_foo_tag");
>>
>> /* tag on a typedef */
>> typedef struct foo foo_t1 __tag("typedef_foo_tag");
>> typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag");
>>
>> /* tag on a function and its parameters */
>> __tag("func_add_tag")
>> int add(int x __tag("param_x_tag"), int y __tag("param_y_tag"))
>> {
>>           return x + y;
>> }
>>
>> /* keep the globals/types alive so they land in BTF */
>> int use(foo_t1 *f, foo_t2 *g)
>> {
>>           return add(global_var + static_var + multi_tag_var, f->a + 
>> g->foo2);
>> }
>>
>> $ /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c 
>> decl_tag.c -o decl_tag.o
>> decl_tag.c:30:1: warning: ‘btf_decl_tag’ attribute does not apply to 
>> types [-Wattributes]
>>      30 | } __tag("struct_foo_tag");
>>         | ^
>>
>> $ bpftool btf dump file decl_tag.o
>> [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
>> [2] STRUCT 'foo' size=8 vlen=2
>>           'a' type_id=1 bits_offset=0
>>           'b' type_id=1 bits_offset=32
>> [3] TYPEDEF 'foo_t1' type_id=2
>> [4] STRUCT '(anon)' size=4 vlen=1
>>           'foo2' type_id=1 bits_offset=0
>> [5] TYPEDEF 'foo_t2' type_id=4
>> [6] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2
>>           'f' type_id=7
>>           'g' type_id=8
>> [7] PTR '(anon)' type_id=3
>> [8] PTR '(anon)' type_id=5
>> [9] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2
>>           'x' type_id=1
>>           'y' type_id=1
>> [10] VAR 'multi_tag_var' type_id=1, linkage=global
>> [11] VAR 'global_var' type_id=1, linkage=global
>> [12] FUNC 'use' type_id=6 linkage=global
>> [13] FUNC 'add' type_id=9 linkage=global
>> [14] DECL_TAG 'global_var_tag' type_id=11 component_idx=-1
>> [15] DECL_TAG 'static_var_tag' type_id=0 component_idx=-1
>> [16] DECL_TAG 'tag_b' type_id=10 component_idx=-1
>> [17] DECL_TAG 'tag_a' type_id=10 component_idx=-1
>> [18] DECL_TAG 'member_a_tag' type_id=2 component_idx=0
>> [19] DECL_TAG 'member_b_tag' type_id=2 component_idx=1
>> [20] DECL_TAG 'param_x_tag' type_id=13 component_idx=0
>> [21] DECL_TAG 'param_y_tag' type_id=13 component_idx=1
>> [22] DECL_TAG 'func_add_tag' type_id=13 component_idx=-1
>> [23] DATASEC '.bss' size=0 vlen=2
>>           type_id=10 offset=0 size=4 (VAR 'multi_tag_var')
>>           type_id=11 offset=0 size=4 (VAR 'global_var')
>> $ bpftool btf dump file decl_tag.o | grep DECL_TAG
>> [14] DECL_TAG 'global_var_tag' type_id=11 component_idx=-1
>> [15] DECL_TAG 'static_var_tag' type_id=0 component_idx=-1
>> [16] DECL_TAG 'tag_b' type_id=10 component_idx=-1
>> [17] DECL_TAG 'tag_a' type_id=10 component_idx=-1
>> [18] DECL_TAG 'member_a_tag' type_id=2 component_idx=0
>> [19] DECL_TAG 'member_b_tag' type_id=2 component_idx=1
>> [20] DECL_TAG 'param_x_tag' type_id=13 component_idx=0
>> [21] DECL_TAG 'param_y_tag' type_id=13 component_idx=1
>> [22] DECL_TAG 'func_add_tag' type_id=13 component_idx=-1
>> $
>>
>> Three decl tags (struct_foo_tag, typedef_foo_tag and typedef_foo2_tag)
>> are missing here:
>
> I'll double check, once I'm back from PTO.
>
>> struct foo {
>>           int a __tag("member_a_tag");
>>           int b __tag("member_b_tag");
>> } __tag("struct_foo_tag");
>>
>> /* tag on a typedef */
>> typedef struct foo foo_t1 __tag("typedef_foo_tag");
>> typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag");
>>
>> $ clang -O2 -g -target bpf -c decl_tag.c -o decl_tag.o
>> $ bpftool btf dump file decl_tag.o
>> [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
>> [2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2
>>           'x' type_id=1
>>           'y' type_id=1
>> [3] FUNC 'add' type_id=2 linkage=global
>> [4] DECL_TAG 'param_x_tag' type_id=3 component_idx=0
>> [5] DECL_TAG 'param_y_tag' type_id=3 component_idx=1
>> [6] DECL_TAG 'func_add_tag' type_id=3 component_idx=-1
>> [7] PTR '(anon)' type_id=8
>> [8] TYPEDEF 'foo_t1' type_id=10
>> [9] DECL_TAG 'typedef_foo_tag' type_id=8 component_idx=-1
>> [10] STRUCT 'foo' size=8 vlen=2
>>           'a' type_id=1 bits_offset=0
>>           'b' type_id=1 bits_offset=32
>> [11] DECL_TAG 'struct_foo_tag' type_id=10 component_idx=-1
>> [12] DECL_TAG 'member_a_tag' type_id=10 component_idx=0
>> [13] DECL_TAG 'member_b_tag' type_id=10 component_idx=1
>> [14] PTR '(anon)' type_id=15
>> [15] TYPEDEF 'foo_t2' type_id=17
>> [16] DECL_TAG 'typedef_foo2_tag' type_id=15 component_idx=-1
>> [17] STRUCT '(anon)' size=4 vlen=1
>>           'foo2' type_id=1 bits_offset=0
>> [18] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2
>>           'f' type_id=7
>>           'g' type_id=14
>> [19] FUNC 'use' type_id=18 linkage=global
>> [20] VAR 'global_var' type_id=1, linkage=global
>> [21] DECL_TAG 'global_var_tag' type_id=20 component_idx=-1
>> [22] VAR 'multi_tag_var' type_id=1, linkage=global
>> [23] DECL_TAG 'tag_a' type_id=22 component_idx=-1
>> [24] DECL_TAG 'tag_b' type_id=22 component_idx=-1
>> [25] DATASEC '.bss' size=0 vlen=2
>>           type_id=20 offset=0 size=4 (VAR 'global_var')
>>           type_id=22 offset=0 size=4 (VAR 'multi_tag_var')
>> $ bpftool btf dump file decl_tag.o | grep DECL_TAG
>> [4] DECL_TAG 'param_x_tag' type_id=3 component_idx=0
>> [5] DECL_TAG 'param_y_tag' type_id=3 component_idx=1
>> [6] DECL_TAG 'func_add_tag' type_id=3 component_idx=-1
>> [9] DECL_TAG 'typedef_foo_tag' type_id=8 component_idx=-1
>> [11] DECL_TAG 'struct_foo_tag' type_id=10 component_idx=-1
>> [12] DECL_TAG 'member_a_tag' type_id=10 component_idx=0
>> [13] DECL_TAG 'member_b_tag' type_id=10 component_idx=1
>> [16] DECL_TAG 'typedef_foo2_tag' type_id=15 component_idx=-1
>> [21] DECL_TAG 'global_var_tag' type_id=20 component_idx=-1
>> [23] DECL_TAG 'tag_a' type_id=22 component_idx=-1
>> [24] DECL_TAG 'tag_b' type_id=22 component_idx=-1
>>
>> DECL_TAG 'static_var_tag' type_id=0 component_idx=-1 is missing in llvm.
>> This is execpted for llvm since 'static_var' is 'inlined' since it is
>> value is 0 and the compiler optimization removed it in function use().
>> In llvm Dwarf->BTF conversion is very late and we only emit survived
>> globals.
>>
>> gcc emits 'static_var_tag' probably in frontend. Emitting
>> 'static_var_tag' is okay, just not used.
>>
>> I think gcc should support
>>    - declaration tag for typedef, and
>>    - declaration tag for the whole struct (like above 'struct_foo_tag')
>> to be compatible with llvm.
>
> OK we will look into this.

Thanks!

>
> Thx,
> -Vineet
>


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

* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
  2026-06-03 20:08   ` Yonghong Song
  2026-06-03 20:42   ` Emil Tsalapatis
@ 2026-06-03 21:41   ` Yonghong Song
  2026-06-17 18:34     ` Vineet Gupta
  2026-06-07  9:54   ` Alan Maguire
  3 siblings, 1 reply; 15+ messages in thread
From: Yonghong Song @ 2026-06-03 21:41 UTC (permalink / raw)
  To: Vineet Gupta, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust



On 6/2/26 12:55 PM, Vineet Gupta wrote:
> 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.
>
> 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.
>
> 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>

LGTM except a nit below.

Acked-by: Yonghong Song <yonghong.song@linux.dev>

[...]

> @@ -943,16 +948,40 @@ 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 for functions/struct/member tags.

For 'struct', gcc is not supported yet.

> + * Pointers 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;
> +}
> +
>
[...]


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

* Re: [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test
  2026-06-02 19:55 ` [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
  2026-06-03 20:44   ` Emil Tsalapatis
@ 2026-06-03 21:52   ` Yonghong Song
  1 sibling, 0 replies; 15+ messages in thread
From: Yonghong Song @ 2026-06-03 21:52 UTC (permalink / raw)
  To: Vineet Gupta, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust



On 6/2/26 12:55 PM, Vineet Gupta wrote:
> 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.
>
> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>

LGTM but missing 'pahole -J' for clang test. See below.

Acked-by: Yonghong Song <yonghong.song@linux.dev>

> ---
>   tests/pfunct-btf-decl-tags.sh | 72 +++++++++++++++++++++++++++--------
>   1 file changed, 56 insertions(+), 16 deletions(-)
>
> diff --git a/tests/pfunct-btf-decl-tags.sh b/tests/pfunct-btf-decl-tags.sh
> index 35884b4e8687..a46aa1f3df55 100755
> --- a/tests/pfunct-btf-decl-tags.sh
> +++ b/tests/pfunct-btf-decl-tags.sh
> @@ -6,24 +6,36 @@
>   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) {}
> @@ -31,7 +43,7 @@ __tag(a) __tag(b)          void bar(void) {}
>   __tag(a)                   void buz(void) {}
>   
>   EOF
> -) | $CLANG --target=bpf -c -g -x c -o $tmpobj -
> +)
>   
>   # tags order is not guaranteed
>   sort_tags=$(cat <<EOF
> @@ -54,16 +66,44 @@ a void buz(void);
>   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 --target=bpf -c -g -x c -o $tmpobj -

missing 'pahole -J $tmpobj 2>/dev/null' here.

> +	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


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

* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
                     ` (2 preceding siblings ...)
  2026-06-03 21:41   ` Yonghong Song
@ 2026-06-07  9:54   ` Alan Maguire
  3 siblings, 0 replies; 15+ messages in thread
From: Alan Maguire @ 2026-06-07  9:54 UTC (permalink / raw)
  To: Vineet Gupta, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Emil Tsalapatis, jose.marchesi,
	David Faust

On 02/06/2026 20:55, Vineet Gupta wrote:
> 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.
> 
> 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.
> 
> 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>

Thanks for the respin! Sorry for the delay took me a while to get set
up with latest gcc. Two things below, but in summary there are a few
things we need to line things up between clang and gcc representations
I think. To test these completely there are some existing btf_loader/pfunct
issues below that I've outlined; I can send patches for those fixes as 
prerequisites, or feel free to roll into a respin, whatever you'd prefer. 

> ---
> 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]
> 

I don't want to introduce scope creep - and I may be missing something -
but it seems like the current changes don't cover decl tags for function
parameters. On this topic, there is an existing bug where decl tags
that have a component_idx reference are not rendered correctly by pfunct
which makes testing that properly impossible today. Parameter decl tags are 
prepended to the function declaration rather than tied to the 
parameter referenced by the component_idx, so we should fix that first.
The root cause is btf_loader.c always attaches attributes to the function,
whereas it should use the component_idx to attach them to the parameter
tag where component_idx >= 0.

The following changes are needed to fix that I think:

diff --git a/btf_loader.c b/btf_loader.c
index b591219..9bb52c3 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 757e499..ab1c381 100644
--- a/dwarves_fprintf.c
+++ b/dwarves_fprintf.c
@@ -1203,6 +1203,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)
@@ -1244,6 +1256,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,
@@ -1252,12 +1265,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 ?: "");
        }
@@ -1409,11 +1424,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)

 
Now with that fix in place, we can add a decl tag parameter case to the pfunct test:

diff --git a/tests/pfunct-btf-decl-tags.sh b/tests/pfunct-btf-decl-tags.sh
index a46aa1f..520148b 100755
--- a/tests/pfunct-btf-decl-tags.sh
+++ b/tests/pfunct-btf-decl-tags.sh
@@ -41,6 +41,7 @@ src=$(cat <<EOF
 __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
 )
@@ -48,13 +49,17 @@ EOF
 # 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
 )
@@ -63,6 +68,7 @@ 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
 )
 
@@ -98,7 +104,8 @@ fi
 
 if [ "$use_clang" -eq 1 ]; then
        tmpobj=$(make_tmpobj)
-       echo "$src" | $CLANG --target=bpf -c -g -x c -o $tmpobj -
+       echo "$src" | $CLANG -c -g -x c -o $tmpobj -
+       pahole -J $tmpobj 2>/dev/null
        run_test "$CLANG" "$tmpobj" || failed=1
 fi
 
Running this we see:

 ./pfunct-btf-decl-tags.sh 
Check that pfunct can print btf_decl_tags read from BTF.
   Testing with gcc (version 17)
   pfunct output does not match expected (gcc (version 17)):
   --- /dev/fd/63 2026-06-07 10:42:19.711753983 +0100 +++ /dev/fd/62 2026-06-07 10:42:19.711753983 +0100 @@ -1,4 +1,4 @@ a b c void foo(void); a b void bar(void); a void buz(void); -void qux(param_a int arg); +void qux(int arg);
   
   Complete output:
   a b c void foo(void); a b void bar(void); a void buz(void); void qux(int arg);
   Testing with clang
   passed
Test ./pfunct-btf-decl-tags.sh failed
Test data is in /tmp/pfunct-btf-decl-tags.sh.hXPG6r

The following change to dwarf_loader.c fixes this:

diff --git a/dwarf_loader.c b/dwarf_loader.c
index c492b56..7f5b44d 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -1910,6 +1910,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 {
                /*


> Changes since v1 [1]
>  - NFC Reduce indentation with early exits (Alexei offlist)
> 
> [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    | 105 +++++++++++++++++++++++++++++++++++++++-------
>  dwarves.h         |   2 +-
>  dwarves_fprintf.c |  12 ++++--
>  5 files changed, 110 insertions(+), 21 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..14f71c9043aa 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,40 @@ 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 for functions/struct/member tags.
> + * Pointers 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,6 +1625,8 @@ 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;
>  }
> @@ -1637,19 +1668,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 */
> @@ -1662,6 +1695,26 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu,
>  			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;
> +
> +		tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf);


I think there may be an issue here. die__add_btf_type_tag() will prepend
discovered tags to give us the BTF representation we want. However a
proof-of-concept test shows that gcc tested with latest master branch
as of

commit 0e634b961123280c17c1c651b1d8b1567b9b523c (HEAD -> master, origin/trunk, origin/master, origin/HEAD)
Author: Marc Poulhiès <poulhies@adacore.com>
Date:   Fri Mar 27 16:29:16 2026 +0100

    ada: Fix bug when reading multibyte utf-8 character
    

...shows that the DWARF representation already has the BTF-desired
order, so we wind up reversing it. Test below, but I _think_ something
like this may be needed:

diff --git a/dwarf_loader.c b/dwarf_loader.c
index 14f71c9..c492b56 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -1633,7 +1633,8 @@ static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *di
 
 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;
@@ -1656,10 +1657,38 @@ 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 the following set of type tags:
+        *
+        * #define __tag(x) __attribute__((btf_type_tag(#x)))
+        *
+        * struct sample {
+        *      int value;
+        * };
+        *
+        * 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"
+        * 
+        * The tag nearest *global_ptr applies to the pointer’s immediate pointee
+        * type first. So inner wraps outer struct sample.
+        *      
+        * For clang DWARF, child annotation DIEs are seen as "outer, inner"
+        * so to get the right final order we need to prepend.
+        *
+        * For GCC DWARF, the explicit DW_AT_GNU_annotation chain starts at the outermost
+        * BTF wrapper:
+        *
+        * pointer DIE -> inner annotation -> outer annotation -> struct sample
+        *
+        * so in this case we preserve that traversal order and append.
         */
-       list_add(&annot->node, &tag->tags);
+       if (prepend)
+               list_add(&annot->node, &tag->tags);
+       else
+               list_add_tail(&annot->node, &tag->tags);
+
        return tag;
 }
 
@@ -1690,7 +1719,7 @@ 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);
+               tag = die__add_btf_type_tag(tag, die, cdie, cu, conf, true);
                if (tag == NULL)
                        return NULL;
        } while (dwarf_siblingof(cdie, cdie) == 0);
@@ -1707,7 +1736,7 @@ check_gnu_attr:
                if (strcmp(name, "btf_type_tag") != 0)
                        break;
 
-               tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf);
+               tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf, false);
                if (tag == NULL)
:


Here's a test (feel free to adapt it if needed) that fails until the above is applied:

#!/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 related	[flat|nested] 15+ messages in thread

* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-03 20:08   ` Yonghong Song
  2026-06-03 20:54     ` Vineet Gupta
@ 2026-06-17 18:18     ` Vineet Gupta
  1 sibling, 0 replies; 15+ messages in thread
From: Vineet Gupta @ 2026-06-17 18:18 UTC (permalink / raw)
  To: Yonghong Song, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust

On 6/3/26 1:08 PM, Yonghong Song wrote:
> I download and build gcc-16.1 which will be used for gcc compilation.
> I did some experiments with this patch set for both type tag and decl tag.

Thx for this doing this exercise.

> For type tag
> ============

[snip]

> So type tag matches between clang and gcc.

Nice !

> For decl tag
> ============
>
> $ cat decl_tag.c
> /* btf_decl_tag test cases.
>    *
>    * btf_decl_tag can be attached to:
>    *   - global (incl. static) variables
>    *   - functions
>    *   - function parameters
>    *   - struct/union types and their members
>    *   - typedefs
>    *
>    * Build:  clang -O2 -target bpf -g -c decl_tag.c -o decl_tag.o
>    *         /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c decl_tag.c -o decl_tag.o
>    * Dump:   bpftool btf dump file decl_tag.o
>    */
>
> #define __tag(x) __attribute__((btf_decl_tag(x)))
>
> /* tag on a global variable */
> int global_var __tag("global_var_tag");
>
> /* tag on a static variable */
> static int static_var __tag("static_var_tag");
>
> /* multiple tags on one declaration */
> int multi_tag_var __tag("tag_a") __tag("tag_b");
>
> /* tag on struct type and its members */
> struct foo {
>           int a __tag("member_a_tag");
>           int b __tag("member_b_tag");
> } __tag("struct_foo_tag");
>
> /* tag on a typedef */
> typedef struct foo foo_t1 __tag("typedef_foo_tag");
> typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag");
>
> /* tag on a function and its parameters */
> __tag("func_add_tag")
> int add(int x __tag("param_x_tag"), int y __tag("param_y_tag"))
> {
>           return x + y;
> }
>
> /* keep the globals/types alive so they land in BTF */
> int use(foo_t1 *f, foo_t2 *g)
> {
>           return add(global_var + static_var + multi_tag_var, f->a + g->foo2);
> }
>
> $ /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c decl_tag.c -o decl_tag.o
> decl_tag.c:30:1: warning: ‘btf_decl_tag’ attribute does not apply to types [-Wattributes]
>      30 | } __tag("struct_foo_tag");
>         | ^
>
[snip]

> Three decl tags (struct_foo_tag, typedef_foo_tag and typedef_foo2_tag)
> are missing here:
>
> struct foo {
>           int a __tag("member_a_tag");
>           int b __tag("member_b_tag");
> } __tag("struct_foo_tag");
>
> /* tag on a typedef */
> typedef struct foo foo_t1 __tag("typedef_foo_tag");
> typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag");

Semantically what does this mean ? Will the decl tag will be "applied" 
where ever the type is instantiated ?

> /* tag on a static variable */
> static int static_var __tag("static_var_tag");
>
> ...
>
> [24] DECL_TAG 'tag_b' type_id=22 component_idx=-1
>
> DECL_TAG 'static_var_tag' type_id=0 component_idx=-1 is missing in llvm.
> This is execpted for llvm since 'static_var' is 'inlined' since it is
> value is 0 and the compiler optimization removed it in function use().
> In llvm Dwarf->BTF conversion is very late and we only emit survived
> globals.
>
> gcc emits 'static_var_tag' probably in frontend. Emitting
> 'static_var_tag' is okay, just not used.

Yeah I recall something like this when talking to David.
We can live with this it seems.

> I think gcc should support
>    - declaration tag for typedef, and
>    - declaration tag for the whole struct (like above 'struct_foo_tag')
> to be compatible with llvm.

OK, opened PR/125862 [1]  for future improvement.

[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125862

Thx,
-Vineet

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

* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-03 21:41   ` Yonghong Song
@ 2026-06-17 18:34     ` Vineet Gupta
  0 siblings, 0 replies; 15+ messages in thread
From: Vineet Gupta @ 2026-06-17 18:34 UTC (permalink / raw)
  To: Yonghong Song, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
	jose.marchesi, David Faust

On 6/3/26 2:41 PM, Yonghong Song wrote:
> LGTM except a nit below.
>
> Acked-by: Yonghong Song <yonghong.song@linux.dev>

Thx !

>> + * Handle gcc style btf_decl_tag annotations for functions/struct/member tags.
> For 'struct', gcc is not supported yet.

Good catch, will fix in respin.

Thx,
-Vineet

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

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

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-02 19:55 [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta
2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
2026-06-03 20:08   ` Yonghong Song
2026-06-03 20:54     ` Vineet Gupta
2026-06-03 21:40       ` Yonghong Song
2026-06-17 18:18     ` Vineet Gupta
2026-06-03 20:42   ` Emil Tsalapatis
2026-06-03 21:41   ` Yonghong Song
2026-06-17 18:34     ` Vineet Gupta
2026-06-07  9:54   ` Alan Maguire
2026-06-02 19:55 ` [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
2026-06-03 20:44   ` Emil Tsalapatis
2026-06-03 21:52   ` Yonghong Song
2026-06-03 20:18 ` [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Yonghong Song
2026-06-03 20:37 ` Emil Tsalapatis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox