All of lore.kernel.org
 help / color / mirror / Atom feed
* [PAHOLE v3 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper
@ 2026-06-01 18:35 Vineet Gupta
  2026-06-01 18:35 ` [PAHOLE v3 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Vineet Gupta @ 2026-06-01 18:35 UTC (permalink / raw)
  To: dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, jose.marchesi,
	David Faust, Vineet Gupta

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

Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
Changes since v2 [2]
 - die__add_btf_type_tag() returns pointer not error code.

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

[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 | 57 +++++++++++++++++++++++++++++---------------------
 1 file changed, 33 insertions(+), 24 deletions(-)

diff --git a/dwarf_loader.c b/dwarf_loader.c
index 16fb7becffee..8b5b526299b5 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -1600,14 +1600,43 @@ 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 +1656,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] 12+ messages in thread

* [PAHOLE v3 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-01 18:35 [PAHOLE v3 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper Vineet Gupta
@ 2026-06-01 18:35 ` Vineet Gupta
  2026-06-02  1:04   ` Emil Tsalapatis
  2026-06-02 18:24   ` Arnaldo Carvalho de Melo
  2026-06-01 18:35 ` [PAHOLE v3 3/3] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
  2026-06-01 19:27 ` [PAHOLE v3 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper Emil Tsalapatis
  2 siblings, 2 replies; 12+ messages in thread
From: Vineet Gupta @ 2026-06-01 18:35 UTC (permalink / raw)
  To: dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, jose.marchesi,
	David Faust, Vineet Gupta

gcc 16 was first release to support DW_TAG_GNU_annotation and this patch
enables the same in pahole. Bulk of changes are in 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, with cycle detection.
- 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.
- Update pfunct-btf-decl-tags.sh test to use GCC 16+ when available and
  now passes.

Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
Changes since v2 [2]
 - Removed loop detection logic
 - Move test changes to different patch

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

[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           |   8 ++++
 dwarf_loader.c    | 102 ++++++++++++++++++++++++++++++++++++++++++----
 dwarves.h         |   3 +-
 dwarves_fprintf.c |   8 +++-
 5 files changed, 110 insertions(+), 12 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..abe0e62b412f 100644
--- a/dutil.h
+++ b/dutil.h
@@ -35,6 +35,14 @@
 #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
+
 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 8b5b526299b5..878565884f85 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -908,6 +908,12 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b
 	return -ENOMEM;
 }
 
+static bool die__tag_is_annotation(Dwarf_Die *die)
+{
+	unsigned int tag = dwarf_tag(die);
+	return tag == DW_TAG_LLVM_annotation || tag == DW_TAG_GNU_annotation;
+}
+
 static int add_llvm_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf,
 			       struct list_head *head)
 {
@@ -943,7 +949,7 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx,
 
 	die = &child;
 	do {
-		if (dwarf_tag(die) == DW_TAG_LLVM_annotation) {
+		if (die__tag_is_annotation(die)) {
 			ret = add_llvm_annotation(die, component_idx, conf, head);
 			if (ret)
 				return ret;
@@ -953,6 +959,35 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx,
 	return 0;
 }
 
+/* Handle gcc sytle btf_decl_tag annotations for functions/struct/member tags
+ * Pointers are handled seperately, 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;
+
+	if (dwarf_attr(die, DW_AT_GNU_annotation, &attr) == NULL ||
+	    dwarf_formref_die(&attr, &annot_die) == NULL)
+		return 0;
+
+	for (;;) {
+		if (dwarf_tag(&annot_die) != DW_TAG_GNU_annotation)
+			break;
+
+		int ret = add_llvm_annotation(&annot_die, component_idx, conf, head);
+		if (ret)
+			return ret;
+
+		if (dwarf_attr(&annot_die, DW_AT_GNU_annotation, &attr) == NULL ||
+		    dwarf_formref_die(&attr, &annot_die) == NULL)
+			break;
+	}
+
+	return 0;
+}
+
 int class_member__dwarf_recode_bitfield(struct class_member *member,
 					struct cu *cu)
 {
@@ -1596,6 +1631,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;
 }
@@ -1636,19 +1673,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 */
+	/* Handle LLVM style annotation tags if present */
+	if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0)
+		goto check_gnu_attr;
+
 	cdie = &child;
 	do {
-		if (dwarf_tag(cdie) != DW_TAG_LLVM_annotation)
+		if (!die__tag_is_annotation(cdie))
 			continue;
 
 		/* Only check btf_type_tag annotations */
@@ -1661,6 +1700,31 @@ 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:
+	/* Check for GCC-style DW_AT_GNU_annotation attribute */
+	if (tag != NULL ||
+	    dwarf_attr(die, DW_AT_GNU_annotation, &attr) == NULL ||
+	    dwarf_formref_die(&attr, &annot_die) == NULL)
+		goto out;
+
+	for (;;) {
+		if (dwarf_tag(&annot_die) != DW_TAG_GNU_annotation)
+			break;
+
+		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;
+
+		if (dwarf_attr(&annot_die, DW_AT_GNU_annotation, &attr) == NULL ||
+		    dwarf_formref_die(&attr, &annot_die) == NULL)
+			break;
+	}
+
+out:
 	return tag ? &tag->tag : tag__new(die, cu);
 }
 
@@ -1689,6 +1753,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;
 }
 
@@ -2050,10 +2120,13 @@ 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:
+		case DW_TAG_GNU_annotation:
 			if (add_llvm_annotation(die, -1, conf, &class->namespace.annots))
 				return -ENOMEM;
 			continue;
@@ -2359,6 +2432,7 @@ static int die__process_function(Dwarf_Die *die, struct ftype *ftype,
 				goto out_enomem;
 			continue;
 		case DW_TAG_LLVM_annotation:
+		case DW_TAG_GNU_annotation:
 			if (add_llvm_annotation(die, -1, conf, &(tag__function(&ftype->tag)->annots)))
 				goto out_enomem;
 			continue;
@@ -2407,6 +2481,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;
 }
 
@@ -2468,6 +2548,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
@@ -2493,7 +2576,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..42b8e39aa2dd 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -670,7 +670,8 @@ 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->tag == DW_TAG_LLVM_annotation ||
+	       tag->tag == DW_TAG_GNU_annotation;
 }
 
 static inline const char *tag__decl_file(const struct tag *tag,
diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c
index 1ec478c2a027..a514d7e98923 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);
@@ -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 || (type->tag != DW_TAG_LLVM_annotation && type->tag != DW_TAG_GNU_annotation) || type->type == id)
 			break;
 		id = type->type;
 	}
@@ -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] 12+ messages in thread

* [PAHOLE v3 3/3] tests: Support GCC in pfunct-btf-decl-tags test
  2026-06-01 18:35 [PAHOLE v3 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper Vineet Gupta
  2026-06-01 18:35 ` [PAHOLE v3 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
@ 2026-06-01 18:35 ` Vineet Gupta
  2026-06-02  1:05   ` Emil Tsalapatis
  2026-06-01 19:27 ` [PAHOLE v3 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper Emil Tsalapatis
  2 siblings, 1 reply; 12+ messages in thread
From: Vineet Gupta @ 2026-06-01 18:35 UTC (permalink / raw)
  To: dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, jose.marchesi,
	David Faust, Vineet Gupta

GCC 16+ supports btf_decl_tag via DW_TAG_GNU_annotation.
Update the test to run with either of GCC (>= 16) and clang.

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

* Re: [PAHOLE v3 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper
  2026-06-01 18:35 [PAHOLE v3 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper Vineet Gupta
  2026-06-01 18:35 ` [PAHOLE v3 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
  2026-06-01 18:35 ` [PAHOLE v3 3/3] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
@ 2026-06-01 19:27 ` Emil Tsalapatis
  2026-06-01 19:44   ` Vineet Gupta
  2026-06-02 18:14   ` Arnaldo Carvalho de Melo
  2 siblings, 2 replies; 12+ messages in thread
From: Emil Tsalapatis @ 2026-06-01 19:27 UTC (permalink / raw)
  To: Vineet Gupta, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, jose.marchesi,
	David Faust

On Mon Jun 1, 2026 at 2:35 PM EDT, Vineet Gupta wrote:
> NFC change preparing for DW_TAG_GNU_annotation support.
> Extract the btf_type_tag annotation creation logic from into a helper
> die__add_btf_type_tag().
>
> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
> ---
> Changes since v2 [2]
>  - die__add_btf_type_tag() returns pointer not error code.
>
> Changes since v1 [1]
>  - NFC reinstate some original comments

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

Minor nit/question below.

>
> [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 | 57 +++++++++++++++++++++++++++++---------------------
>  1 file changed, 33 insertions(+), 24 deletions(-)
>
> diff --git a/dwarf_loader.c b/dwarf_loader.c
> index 16fb7becffee..8b5b526299b5 100644
> --- a/dwarf_loader.c
> +++ b/dwarf_loader.c
> @@ -1600,14 +1600,43 @@ 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,

Not familiar with pahole's coding style, do we want to adjust the
comments?

> +	 * 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 +1656,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] 12+ messages in thread

* Re: [PAHOLE v3 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper
  2026-06-01 19:27 ` [PAHOLE v3 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper Emil Tsalapatis
@ 2026-06-01 19:44   ` Vineet Gupta
  2026-06-02 18:14   ` Arnaldo Carvalho de Melo
  1 sibling, 0 replies; 12+ messages in thread
From: Vineet Gupta @ 2026-06-01 19:44 UTC (permalink / raw)
  To: Emil Tsalapatis, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, jose.marchesi,
	David Faust

On 6/1/26 12:27 PM, Emil Tsalapatis wrote:
>> +	/* Prepends: for annotations tag1 -> tag2 -> tag3,
> Not familiar with pahole's coding style, do we want to adjust the
> comments?

I just tweaked whatever was the style before for that comment block.
Pahole it seems has a mix of the 2 styles: linux kernel type with empty 
first and last lines vs. only empty last line.

Thx for taking a look.

Thx,
-Vineet

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

* Re: [PAHOLE v3 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-01 18:35 ` [PAHOLE v3 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
@ 2026-06-02  1:04   ` Emil Tsalapatis
  2026-06-02 18:54     ` Vineet Gupta
  2026-06-02 18:24   ` Arnaldo Carvalho de Melo
  1 sibling, 1 reply; 12+ messages in thread
From: Emil Tsalapatis @ 2026-06-02  1:04 UTC (permalink / raw)
  To: Vineet Gupta, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, jose.marchesi,
	David Faust

On Mon Jun 1, 2026 at 2:35 PM EDT, Vineet Gupta wrote:
> gcc 16 was first release to support DW_TAG_GNU_annotation and this patch
> enables the same in pahole. Bulk of changes are in 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, with cycle detection.
> - 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.
> - Update pfunct-btf-decl-tags.sh test to use GCC 16+ when available and
>   now passes.
>
> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
> ---
> Changes since v2 [2]
>  - Removed loop detection logic
>  - Move test changes to different patch
>
> Changes since v1 [1]
>  - NFC Reduce indentation with early exits (Alexei offlist)
>
> [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           |   8 ++++
>  dwarf_loader.c    | 102 ++++++++++++++++++++++++++++++++++++++++++----
>  dwarves.h         |   3 +-
>  dwarves_fprintf.c |   8 +++-
>  5 files changed, 110 insertions(+), 12 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..abe0e62b412f 100644
> --- a/dutil.h
> +++ b/dutil.h
> @@ -35,6 +35,14 @@
>  #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
> +
>  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 8b5b526299b5..878565884f85 100644
> --- a/dwarf_loader.c
> +++ b/dwarf_loader.c
> @@ -908,6 +908,12 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b
>  	return -ENOMEM;
>  }
>  
> +static bool die__tag_is_annotation(Dwarf_Die *die)
> +{
> +	unsigned int tag = dwarf_tag(die);
> +	return tag == DW_TAG_LLVM_annotation || tag == DW_TAG_GNU_annotation;

Can we also have a shorthand static inline for

tag == DW_TAG_LLVM_annotation || tag == DW_TAG_GNU_annotation

? Would be nice in a bunch of places.

> +}
> +
>  static int add_llvm_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf,
>  			       struct list_head *head)

>  {
> @@ -943,7 +949,7 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx,

Since this is not just LLVM annotations can we rename it to
*_tag_annotations or something similar? Same for all other llvm-named
fucntions we are explicitly handling GCC tags in.

>  
>  	die = &child;
>  	do {
> -		if (dwarf_tag(die) == DW_TAG_LLVM_annotation) {
> +		if (die__tag_is_annotation(die)) {
>  			ret = add_llvm_annotation(die, component_idx, conf, head);
>  			if (ret)
>  				return ret;
> @@ -953,6 +959,35 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx,
>  	return 0;
>  }
>  
> +/* Handle gcc sytle btf_decl_tag annotations for functions/struct/member tags

Typo: sytle -> style

> + * Pointers are handled seperately, inline in die__create_new_pointer_tag ()

Typo: seperately -> separately, also maybe remove the space before the ()

> + */
> +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;
> +
> +	if (dwarf_attr(die, DW_AT_GNU_annotation, &attr) == NULL ||
> +	    dwarf_formref_die(&attr, &annot_die) == NULL)
> +		return 0;
> +
> +	for (;;) {
> +		if (dwarf_tag(&annot_die) != DW_TAG_GNU_annotation)
> +			break;
> +
> +		int ret = add_llvm_annotation(&annot_die, component_idx, conf, head);

E.g., here imo it makes sense to keep the llvm name since AFAICT this is
one of the places where we normalize the GCC tags.

> +		if (ret)
> +			return ret;
> +
> +		if (dwarf_attr(&annot_die, DW_AT_GNU_annotation, &attr) == NULL ||
> +		    dwarf_formref_die(&attr, &annot_die) == NULL)
> +			break;
> +	}
> +
> +	return 0;
> +}
> +
>  int class_member__dwarf_recode_bitfield(struct class_member *member,
>  					struct cu *cu)
>  {
> @@ -1596,6 +1631,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;
>  }
> @@ -1636,19 +1673,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 */
> +	/* Handle LLVM style annotation tags if present */
Can we move this comment to right below the goto? It's slightly
confusing here.

For that matter, why are we processing LLVM and GNU tags in succession?
Could we just turn check_gnu_attr into a separate function and instead
of a goto do 

return check_gnu_attr();

?

Then again mixing the two types of tags seems theoretically possible
and the least surprising thing to do is handle both.

> +	if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0)
> +		goto check_gnu_attr;
> +
>  	cdie = &child;
>  	do {
> -		if (dwarf_tag(cdie) != DW_TAG_LLVM_annotation)
> +		if (!die__tag_is_annotation(cdie))
>  			continue;
>  
>  		/* Only check btf_type_tag annotations */
> @@ -1661,6 +1700,31 @@ 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:
> +	/* Check for GCC-style DW_AT_GNU_annotation attribute */
> +	if (tag != NULL ||
> +	    dwarf_attr(die, DW_AT_GNU_annotation, &attr) == NULL ||
> +	    dwarf_formref_die(&attr, &annot_die) == NULL)
> +		goto out;
> +
> +	for (;;) {
> +		if (dwarf_tag(&annot_die) != DW_TAG_GNU_annotation)
> +			break;
> +
> +		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;
> +
> +		if (dwarf_attr(&annot_die, DW_AT_GNU_annotation, &attr) == NULL ||
> +		    dwarf_formref_die(&attr, &annot_die) == NULL)
> +			break;
> +	}
> +
> +out:
>  	return tag ? &tag->tag : tag__new(die, cu);
>  }
>  
> @@ -1689,6 +1753,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;
>  }
>  
> @@ -2050,10 +2120,13 @@ 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:
> +		case DW_TAG_GNU_annotation:
>  			if (add_llvm_annotation(die, -1, conf, &class->namespace.annots))
>  				return -ENOMEM;
>  			continue;
> @@ -2359,6 +2432,7 @@ static int die__process_function(Dwarf_Die *die, struct ftype *ftype,
>  				goto out_enomem;
>  			continue;
>  		case DW_TAG_LLVM_annotation:
> +		case DW_TAG_GNU_annotation:
>  			if (add_llvm_annotation(die, -1, conf, &(tag__function(&ftype->tag)->annots)))
>  				goto out_enomem;
>  			continue;
> @@ -2407,6 +2481,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;
>  }
>  
> @@ -2468,6 +2548,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
> @@ -2493,7 +2576,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..42b8e39aa2dd 100644
> --- a/dwarves.h
> +++ b/dwarves.h
> @@ -670,7 +670,8 @@ 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->tag == DW_TAG_LLVM_annotation ||
> +	       tag->tag == DW_TAG_GNU_annotation;

Another place where we can just have an "is annotation tag" predicate.

>  }
>  
>  static inline const char *tag__decl_file(const struct tag *tag,
> diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c
> index 1ec478c2a027..a514d7e98923 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);
> @@ -731,7 +734,7 @@ static type_id_t skip_llvm_annotations(const struct cu *cu, type_id_t id)

Rename this function to not be llvm-specific anymore?

>  		if (id == 0)
>  			break;
>  		type = cu__type(cu, id);
> -		if (type == NULL || type->tag != DW_TAG_LLVM_annotation || type->type == id)
> +		if (type == NULL || (type->tag != DW_TAG_LLVM_annotation && type->tag != DW_TAG_GNU_annotation) || type->type == id)

Same as before wrt predicate

>  			break;
>  		id = type->type;
>  	}
> @@ -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] 12+ messages in thread

* Re: [PAHOLE v3 3/3] tests: Support GCC in pfunct-btf-decl-tags test
  2026-06-01 18:35 ` [PAHOLE v3 3/3] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
@ 2026-06-02  1:05   ` Emil Tsalapatis
  0 siblings, 0 replies; 12+ messages in thread
From: Emil Tsalapatis @ 2026-06-02  1:05 UTC (permalink / raw)
  To: Vineet Gupta, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, jose.marchesi,
	David Faust

On Mon Jun 1, 2026 at 2:35 PM EDT, Vineet Gupta wrote:
> GCC 16+ supports btf_decl_tag via DW_TAG_GNU_annotation.
> Update the test to run with either of GCC (>= 16) and clang.
>
> 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] 12+ messages in thread

* Re: [PAHOLE v3 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper
  2026-06-01 19:27 ` [PAHOLE v3 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper Emil Tsalapatis
  2026-06-01 19:44   ` Vineet Gupta
@ 2026-06-02 18:14   ` Arnaldo Carvalho de Melo
  2026-06-02 19:00     ` Vineet Gupta
  1 sibling, 1 reply; 12+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-06-02 18:14 UTC (permalink / raw)
  To: Emil Tsalapatis
  Cc: Vineet Gupta, dwarves, bpf, Andrii Nakryiko, Alan Maguire,
	jose.marchesi, David Faust

On Mon, Jun 01, 2026 at 03:27:41PM -0400, Emil Tsalapatis wrote:
> On Mon Jun 1, 2026 at 2:35 PM EDT, Vineet Gupta wrote:
> > NFC change preparing for DW_TAG_GNU_annotation support.
> > Extract the btf_type_tag annotation creation logic from into a helper
> > die__add_btf_type_tag().
> >
> > Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
> > ---
> > Changes since v2 [2]
> >  - die__add_btf_type_tag() returns pointer not error code.
> >
> > Changes since v1 [1]
> >  - NFC reinstate some original comments
> 
> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
 
> Minor nit/question below.

Some more nits below :-)
 
> > +++ b/dwarf_loader.c
> > @@ -1600,14 +1600,43 @@ 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;

There was an inconsistency on the code you moved here, namely first tag
is tested against NULL then it is negated, equivalent, but since we're
moving it, lets make it consistent, in the kernel it is most common to
use !ptr as it is more compact, so please make the first test !tag, just
like the second.

> > +	}
> > +
> > +	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,
 
> Not familiar with pahole's coding style, do we want to adjust the
> comments?

Not that important, but we try to follow kernel style, so yeah that
would be:

	/*
	 * Prepends: for annotations tag1 -> tag2 -> tag3,

But by now I don't dwell that much on these minor details :)

> > +	 * 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 +1656,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] 12+ messages in thread

* Re: [PAHOLE v3 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-01 18:35 ` [PAHOLE v3 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
  2026-06-02  1:04   ` Emil Tsalapatis
@ 2026-06-02 18:24   ` Arnaldo Carvalho de Melo
  2026-06-02 19:23     ` Vineet Gupta
  1 sibling, 1 reply; 12+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-06-02 18:24 UTC (permalink / raw)
  To: Vineet Gupta
  Cc: dwarves, bpf, Andrii Nakryiko, Alan Maguire, jose.marchesi,
	David Faust

On Mon, Jun 01, 2026 at 11:35:10AM -0700, Vineet Gupta wrote:
> gcc 16 was first release to support DW_TAG_GNU_annotation and this patch
> enables the same in pahole. Bulk of changes are in 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, with cycle detection.
> - 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.
> - Update pfunct-btf-decl-tags.sh test to use GCC 16+ when available and
>   now passes.
> 
> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
> ---
> Changes since v2 [2]
>  - Removed loop detection logic
>  - Move test changes to different patch
> 
> Changes since v1 [1]
>  - NFC Reduce indentation with early exits (Alexei offlist)
> 
> [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           |   8 ++++
>  dwarf_loader.c    | 102 ++++++++++++++++++++++++++++++++++++++++++----
>  dwarves.h         |   3 +-
>  dwarves_fprintf.c |   8 +++-
>  5 files changed, 110 insertions(+), 12 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..abe0e62b412f 100644
> --- a/dutil.h
> +++ b/dutil.h
> @@ -35,6 +35,14 @@
>  #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
> +
>  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 8b5b526299b5..878565884f85 100644
> --- a/dwarf_loader.c
> +++ b/dwarf_loader.c
> @@ -908,6 +908,12 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b
>  	return -ENOMEM;
>  }
>  
> +static bool die__tag_is_annotation(Dwarf_Die *die)
> +{
> +	unsigned int tag = dwarf_tag(die);
> +	return tag == DW_TAG_LLVM_annotation || tag == DW_TAG_GNU_annotation;
> +}
> +
>  static int add_llvm_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf,
>  			       struct list_head *head)
>  {
> @@ -943,7 +949,7 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx,
>  
>  	die = &child;
>  	do {
> -		if (dwarf_tag(die) == DW_TAG_LLVM_annotation) {
> +		if (die__tag_is_annotation(die)) {
>  			ret = add_llvm_annotation(die, component_idx, conf, head);
>  			if (ret)
>  				return ret;
> @@ -953,6 +959,35 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx,
>  	return 0;
>  }
>  
> +/* Handle gcc sytle btf_decl_tag annotations for functions/struct/member tags
> + * Pointers are handled seperately, 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;
> +
> +	if (dwarf_attr(die, DW_AT_GNU_annotation, &attr) == NULL ||
> +	    dwarf_formref_die(&attr, &annot_die) == NULL)
> +		return 0;
> +
> +	for (;;) {
> +		if (dwarf_tag(&annot_die) != DW_TAG_GNU_annotation)
> +			break;
> +
> +		int ret = add_llvm_annotation(&annot_die, component_idx, conf, head);
> +		if (ret)
> +			return ret;
> +
> +		if (dwarf_attr(&annot_die, DW_AT_GNU_annotation, &attr) == NULL ||
> +		    dwarf_formref_die(&attr, &annot_die) == NULL)
> +			break;

Make this more compact, no need to have these two if blocks, one
suffices:

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;
	int ret = 0;

	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) {
		if ((ret = add_llvm_annotation(&annot_die, component_idx, conf, head)) != 0)
			break;
	}

	return ret;
}


> +	}
> +
> +	return 0;
> +}
> +
>  int class_member__dwarf_recode_bitfield(struct class_member *member,
>  					struct cu *cu)
>  {
> @@ -1596,6 +1631,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;
>  }
> @@ -1636,19 +1673,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 */
> +	/* Handle LLVM style annotation tags if present */
> +	if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0)
> +		goto check_gnu_attr;
> +
>  	cdie = &child;
>  	do {
> -		if (dwarf_tag(cdie) != DW_TAG_LLVM_annotation)
> +		if (!die__tag_is_annotation(cdie))
>  			continue;
>  
>  		/* Only check btf_type_tag annotations */
> @@ -1661,6 +1700,31 @@ 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:
> +	/* Check for GCC-style DW_AT_GNU_annotation attribute */
> +	if (tag != NULL ||
> +	    dwarf_attr(die, DW_AT_GNU_annotation, &attr) == NULL ||
> +	    dwarf_formref_die(&attr, &annot_die) == NULL)
> +		goto out;
> +
> +	for (;;) {
> +		if (dwarf_tag(&annot_die) != DW_TAG_GNU_annotation)
> +			break;
> +
> +		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;
> +
> +		if (dwarf_attr(&annot_die, DW_AT_GNU_annotation, &attr) == NULL ||
> +		    dwarf_formref_die(&attr, &annot_die) == NULL)
> +			break;
> +	}

The loop above probably can get some simplification?

> +
> +out:
>  	return tag ? &tag->tag : tag__new(die, cu);
>  }
>  
> @@ -1689,6 +1753,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;
>  }
>  
> @@ -2050,10 +2120,13 @@ 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:
> +		case DW_TAG_GNU_annotation:
>  			if (add_llvm_annotation(die, -1, conf, &class->namespace.annots))
>  				return -ENOMEM;
>  			continue;
> @@ -2359,6 +2432,7 @@ static int die__process_function(Dwarf_Die *die, struct ftype *ftype,
>  				goto out_enomem;
>  			continue;
>  		case DW_TAG_LLVM_annotation:
> +		case DW_TAG_GNU_annotation:
>  			if (add_llvm_annotation(die, -1, conf, &(tag__function(&ftype->tag)->annots)))
>  				goto out_enomem;
>  			continue;
> @@ -2407,6 +2481,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;
>  }
>  
> @@ -2468,6 +2548,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
> @@ -2493,7 +2576,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..42b8e39aa2dd 100644
> --- a/dwarves.h
> +++ b/dwarves.h
> @@ -670,7 +670,8 @@ 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->tag == DW_TAG_LLVM_annotation ||
> +	       tag->tag == DW_TAG_GNU_annotation;
>  }
>  
>  static inline const char *tag__decl_file(const struct tag *tag,
> diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c
> index 1ec478c2a027..a514d7e98923 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);
> @@ -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 || (type->tag != DW_TAG_LLVM_annotation && type->tag != DW_TAG_GNU_annotation) || type->type == id)
>  			break;
>  		id = type->type;
>  	}
> @@ -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	[flat|nested] 12+ messages in thread

* Re: [PAHOLE v3 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-02  1:04   ` Emil Tsalapatis
@ 2026-06-02 18:54     ` Vineet Gupta
  0 siblings, 0 replies; 12+ messages in thread
From: Vineet Gupta @ 2026-06-02 18:54 UTC (permalink / raw)
  To: Emil Tsalapatis, dwarves
  Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, jose.marchesi,
	David Faust

Hi Emil,

On 6/1/26 6:04 PM, Emil Tsalapatis wrote:
>>   }
>>   
>> +static bool die__tag_is_annotation(Dwarf_Die *die)
>> +{
>> +	unsigned int tag = dwarf_tag(die);
>> +	return tag == DW_TAG_LLVM_annotation || tag == DW_TAG_GNU_annotation;
> Can we also have a shorthand static inline for
>
> tag == DW_TAG_LLVM_annotation || tag == DW_TAG_GNU_annotation
>
> ? Would be nice in a bunch of places.

OK.
>
>> +}
>> +
>>   static int add_llvm_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf,
>>   			       struct list_head *head)

This one is used by both so I renamed it to add_tag_annotation.

>>   {
>> @@ -943,7 +949,7 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx,
> Since this is not just LLVM annotations can we rename it to
> *_tag_annotations or something similar? Same for all other llvm-named
> fucntions we are explicitly handling GCC tags in.

This is llvm specific, since the child style annotations are generated 
by llvm only. So the current name makes sense.

>>   	die = &child;
>>   	do {
>> -		if (dwarf_tag(die) == DW_TAG_LLVM_annotation) {
>> +		if (die__tag_is_annotation(die)) {
>>   			ret = add_llvm_annotation(die, component_idx, conf, head);
>>   			if (ret)
>>   				return ret;
>> @@ -953,6 +959,35 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx,
>>   	return 0;
>>   }
>>   
>> +/* Handle gcc sytle btf_decl_tag annotations for functions/struct/member tags
> Typo: sytle -> style

Fixed.

>> + * Pointers are handled seperately, inline in die__create_new_pointer_tag ()
> Typo: seperately -> separately, also maybe remove the space before the ()

Fixed.

>> + */
>> +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;
>> +
>> +	if (dwarf_attr(die, DW_AT_GNU_annotation, &attr) == NULL ||
>> +	    dwarf_formref_die(&attr, &annot_die) == NULL)
>> +		return 0;
>> +
>> +	for (;;) {
>> +		if (dwarf_tag(&annot_die) != DW_TAG_GNU_annotation)
>> +			break;
>> +
>> +		int ret = add_llvm_annotation(&annot_die, component_idx, conf, head);
> E.g., here imo it makes sense to keep the llvm name since AFAICT this is
> one of the places where we normalize the GCC tags.

Normalization is just an internal detail, this is actually using the 
common functionality which we already renamed above to the agnostic name 
add_tag_annotation.

>> -	/* 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 */
>> +	/* Handle LLVM style annotation tags if present */
> Can we move this comment to right below the goto? It's slightly
> confusing here.

Yep makes sense. Also for consistency did the same for the GNU comment.

> For that matter, why are we processing LLVM and GNU tags in succession?
> Could we just turn check_gnu_attr into a separate function and instead
> of a goto do
>
> return check_gnu_attr();
>
> ?
>
> Then again mixing the two types of tags seems theoretically possible
> and the least surprising thing to do is handle both.

Code to handle both needs to exist in some shape and form, we just do it 
one after other, rather then in an if .. else.
If LLVM style handling happens, @tag at the end of llvm loop will be non 
null and it won't enter the gnu block, its clear enough from looking at 
the code IMO.
Carving out another function feels a bit excessive.

>>   		}
>> diff --git a/dwarves.h b/dwarves.h
>> index 5ec16e750e83..42b8e39aa2dd 100644
>> --- a/dwarves.h
>> +++ b/dwarves.h
>> @@ -670,7 +670,8 @@ 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->tag == DW_TAG_LLVM_annotation ||
>> +	       tag->tag == DW_TAG_GNU_annotation;
> Another place where we can just have an "is annotation tag" predicate.

Fixed.

>
>> @@ -731,7 +734,7 @@ static type_id_t skip_llvm_annotations(const struct cu *cu, type_id_t id)
> Rename this function to not be llvm-specific anymore?

OK.

>
>>   		if (id == 0)
>>   			break;
>>   		type = cu__type(cu, id);
>> -		if (type == NULL || type->tag != DW_TAG_LLVM_annotation || type->type == id)
>> +		if (type == NULL || (type->tag != DW_TAG_LLVM_annotation && type->tag != DW_TAG_GNU_annotation) || type->type == id)
> Same as before wrt predicate

OK.

Thx for the good feedback.
-Vineet

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

* Re: [PAHOLE v3 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper
  2026-06-02 18:14   ` Arnaldo Carvalho de Melo
@ 2026-06-02 19:00     ` Vineet Gupta
  0 siblings, 0 replies; 12+ messages in thread
From: Vineet Gupta @ 2026-06-02 19:00 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Emil Tsalapatis
  Cc: dwarves, bpf, Andrii Nakryiko, Alan Maguire, jose.marchesi,
	David Faust

On 6/2/26 11:14 AM, Arnaldo Carvalho de Melo wrote:
> On Mon, Jun 01, 2026 at 03:27:41PM -0400, Emil Tsalapatis wrote:
>> On Mon Jun 1, 2026 at 2:35 PM EDT, Vineet Gupta wrote:
>>> NFC change preparing for DW_TAG_GNU_annotation support.
>>> Extract the btf_type_tag annotation creation logic from into a helper
>>> die__add_btf_type_tag().
>>>
>>> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
>>> ---
>>> Changes since v2 [2]
>>>   - die__add_btf_type_tag() returns pointer not error code.
>>>
>>> Changes since v1 [1]
>>>   - NFC reinstate some original comments
>> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
>   
>> Minor nit/question below.
> Some more nits below :-)
>   
>>> +++ b/dwarf_loader.c
>>> @@ -1600,14 +1600,43 @@ 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;
> There was an inconsistency on the code you moved here, namely first tag
> is tested against NULL then it is negated, equivalent, but since we're
> moving it, lets make it consistent, in the kernel it is most common to
> use !ptr as it is more compact, so please make the first test !tag, just
> like the second.

Thx, I already spotted that and fixed for v4.

>
>>> +	}
>>> +
>>> +	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,
>   
>> Not familiar with pahole's coding style, do we want to adjust the
>> comments?
> Not that important, but we try to follow kernel style, so yeah that
> would be:
>
> 	/*
> 	 * Prepends: for annotations tag1 -> tag2 -> tag3,
>
> But by now I don't dwell that much on these minor details :)

Ok will fix it.

Thx,
-Vineet

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

* Re: [PAHOLE v3 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation
  2026-06-02 18:24   ` Arnaldo Carvalho de Melo
@ 2026-06-02 19:23     ` Vineet Gupta
  0 siblings, 0 replies; 12+ messages in thread
From: Vineet Gupta @ 2026-06-02 19:23 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: dwarves, bpf, Andrii Nakryiko, Alan Maguire, jose.marchesi,
	David Faust

On 6/2/26 11:24 AM, Arnaldo Carvalho de Melo wrote:
> On Mon, Jun 01, 2026 at 11:35:10AM -0700, Vineet Gupta wrote:
>> gcc 16 was first release to support DW_TAG_GNU_annotation and this patch
>> enables the same in pahole. Bulk of changes are in 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, with cycle detection.
>> - 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.
>> - Update pfunct-btf-decl-tags.sh test to use GCC 16+ when available and
>>    now passes.
>>
>> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
>> ---
>> Changes since v2 [2]
>>   - Removed loop detection logic
>>   - Move test changes to different patch
>>
>> Changes since v1 [1]
>>   - NFC Reduce indentation with early exits (Alexei offlist)
>>
>> [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           |   8 ++++
>>   dwarf_loader.c    | 102 ++++++++++++++++++++++++++++++++++++++++++----
>>   dwarves.h         |   3 +-
>>   dwarves_fprintf.c |   8 +++-
>>   5 files changed, 110 insertions(+), 12 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..abe0e62b412f 100644
>> --- a/dutil.h
>> +++ b/dutil.h
>> @@ -35,6 +35,14 @@
>>   #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
>> +
>>   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 8b5b526299b5..878565884f85 100644
>> --- a/dwarf_loader.c
>> +++ b/dwarf_loader.c
>> @@ -908,6 +908,12 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b
>>   	return -ENOMEM;
>>   }
>>   
>> +static bool die__tag_is_annotation(Dwarf_Die *die)
>> +{
>> +	unsigned int tag = dwarf_tag(die);
>> +	return tag == DW_TAG_LLVM_annotation || tag == DW_TAG_GNU_annotation;
>> +}
>> +
>>   static int add_llvm_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf,
>>   			       struct list_head *head)
>>   {
>> @@ -943,7 +949,7 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx,
>>   
>>   	die = &child;
>>   	do {
>> -		if (dwarf_tag(die) == DW_TAG_LLVM_annotation) {
>> +		if (die__tag_is_annotation(die)) {
>>   			ret = add_llvm_annotation(die, component_idx, conf, head);
>>   			if (ret)
>>   				return ret;
>> @@ -953,6 +959,35 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx,
>>   	return 0;
>>   }
>>   
>> +/* Handle gcc sytle btf_decl_tag annotations for functions/struct/member tags
>> + * Pointers are handled seperately, 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;
>> +
>> +	if (dwarf_attr(die, DW_AT_GNU_annotation, &attr) == NULL ||
>> +	    dwarf_formref_die(&attr, &annot_die) == NULL)
>> +		return 0;
>> +
>> +	for (;;) {
>> +		if (dwarf_tag(&annot_die) != DW_TAG_GNU_annotation)
>> +			break;
>> +
>> +		int ret = add_llvm_annotation(&annot_die, component_idx, conf, head);
>> +		if (ret)
>> +			return ret;
>> +
>> +		if (dwarf_attr(&annot_die, DW_AT_GNU_annotation, &attr) == NULL ||
>> +		    dwarf_formref_die(&attr, &annot_die) == NULL)
>> +			break;
> Make this more compact, no need to have these two if blocks, one
> suffices:
>
> 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;
> 	int ret = 0;
>
> 	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) {
> 		if ((ret = add_llvm_annotation(&annot_die, component_idx, conf, head)) != 0)
> 			break;
> 	}
>
> 	return ret;
> }

OK.
That duplication indeed was standing out, and while this seems denser to 
read, it feels like an improvement.

FWIW it does need an additional following line right before loop end 
since they were different in the orig version.
     die = &annot_die



>> +check_gnu_attr:
>> +	/* Check for GCC-style DW_AT_GNU_annotation attribute */
>> +	if (tag != NULL ||
>> +	    dwarf_attr(die, DW_AT_GNU_annotation, &attr) == NULL ||
>> +	    dwarf_formref_die(&attr, &annot_die) == NULL)
>> +		goto out;
>> +
>> +	for (;;) {
>> +		if (dwarf_tag(&annot_die) != DW_TAG_GNU_annotation)
>> +			break;
>> +
>> +		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;
>> +
>> +		if (dwarf_attr(&annot_die, DW_AT_GNU_annotation, &attr) == NULL ||
>> +		    dwarf_formref_die(&attr, &annot_die) == NULL)
>> +			break;
>> +	}
> The loop above probably can get some simplification?

Yep nice and similar to above, with same adjustment to @die.

Thx for the review.
-Vineet

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

end of thread, other threads:[~2026-06-02 19:23 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-01 18:35 [PAHOLE v3 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper Vineet Gupta
2026-06-01 18:35 ` [PAHOLE v3 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
2026-06-02  1:04   ` Emil Tsalapatis
2026-06-02 18:54     ` Vineet Gupta
2026-06-02 18:24   ` Arnaldo Carvalho de Melo
2026-06-02 19:23     ` Vineet Gupta
2026-06-01 18:35 ` [PAHOLE v3 3/3] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
2026-06-02  1:05   ` Emil Tsalapatis
2026-06-01 19:27 ` [PAHOLE v3 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper Emil Tsalapatis
2026-06-01 19:44   ` Vineet Gupta
2026-06-02 18:14   ` Arnaldo Carvalho de Melo
2026-06-02 19:00     ` Vineet Gupta

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