* [PAHOLE v5 0/5] support for DW_TAG_GNU_annotation and fixes
@ 2026-06-18 0:57 Vineet Gupta
2026-06-18 0:57 ` [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters Vineet Gupta
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Vineet Gupta @ 2026-06-18 0:57 UTC (permalink / raw)
To: dwarves
Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
jose.marchesi, David Faust, Yonghong Song, Vineet Gupta
Hi,
This series handles gcc generated DW_TAG_GNU_annotation with the end
goal to finally enable CONFIG_PAHOLE_HAS_BTF_TAG for gcc kerenel builds.
Patches 1/5 and 5/5 are newly introduced in v5 per Alan's ask.
P.S.: I've optimistically included Reviewed-by/Acked-by from Emil and
Yonghong respectively on v4 [4] but those were before additional changes
in v5 from Alan for a few things. Please let me know if that's no longer
true.
[4] https://lore.kernel.org/bpf/20260602195512.1511013-1-vineet.gupta@linux.dev/
Thx,
-Vineet
Alan Maguire (2):
btf_loader: Handle decl tag component_idx for parameters
tests: Add btf_type_tag ordering test
Vineet Gupta (3):
dwarf_loader: Extract die__add_btf_type_tag() helper [NFC]
dwarf_loader: Add support for DW_TAG_GNU_annotation
tests: Support GCC in pfunct-btf-decl-tags test
btf_encoder.c | 1 +
btf_loader.c | 46 ++++++++-
dutil.h | 11 ++
dwarf_loader.c | 185 +++++++++++++++++++++++++++-------
dwarves.h | 2 +-
dwarves_fprintf.c | 32 ++++--
tests/btf_type_tag_order.sh | 179 ++++++++++++++++++++++++++++++++
tests/pfunct-btf-decl-tags.sh | 93 ++++++++++++-----
8 files changed, 478 insertions(+), 71 deletions(-)
create mode 100755 tests/btf_type_tag_order.sh
--
2.54.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters
2026-06-18 0:57 [PAHOLE v5 0/5] support for DW_TAG_GNU_annotation and fixes Vineet Gupta
@ 2026-06-18 0:57 ` Vineet Gupta
2026-06-18 4:46 ` Emil Tsalapatis
2026-06-18 0:57 ` [PAHOLE v5 2/5] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Vineet Gupta @ 2026-06-18 0:57 UTC (permalink / raw)
To: dwarves
Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
jose.marchesi, David Faust, Yonghong Song, Vineet Gupta
From: Alan Maguire <alan.maguire@oracle.com>
A BTF_KIND_DECL_TAG with a non-negative component_idx applies to a
specific function parameter (or struct/union member), not to the
function itself. btf_loader.c however attached every decl tag to the
type named by btf_type->type, so parameter decl tags were recorded on
the function rather than on the parameter, and pfunct never printed
them with the parameter.
Resolve a non-negative component_idx to the corresponding parameter via
new helpers ftype__parameter()/function__parameter(), and attach the
tag there. Teach the pretty printer to emit a parameter's attributes by
factoring the function-level attribute loop into tag__attributes_fprintf()
and calling it from ftype__fprintf_parms() for each parameter.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
btf_loader.c | 46 +++++++++++++++++++++++++++++++++++++++++++++-
dwarves_fprintf.c | 20 ++++++++++++++++----
2 files changed, 61 insertions(+), 5 deletions(-)
diff --git a/btf_loader.c b/btf_loader.c
index b591219a245d..9bb52c3c5801 100644
--- a/btf_loader.c
+++ b/btf_loader.c
@@ -477,12 +477,56 @@ static struct attributes *attributes__realloc(struct attributes *attributes, con
return result;
}
+static struct tag *ftype__parameter(const struct ftype *ftype, int component_idx)
+{
+ struct parameter *pos;
+ int idx = 0;
+
+ ftype__for_each_parameter(ftype, pos) {
+ if (idx == component_idx)
+ return &pos->tag;
+ ++idx;
+ }
+
+ return NULL;
+}
+
+static struct tag *function__parameter(const struct function *func, struct cu *cu,
+ int component_idx)
+{
+ struct tag *tag;
+
+ if (component_idx < 0)
+ return NULL;
+
+ tag = cu__type(cu, func->proto.tag.type);
+ if (tag == NULL)
+ return NULL;
+
+ return ftype__parameter(tag__ftype(tag), component_idx);
+}
+
static int process_decl_tag(struct cu *cu, const struct btf_type *tp)
{
+ int component_idx = btf_decl_tag(tp)->component_idx;
struct tag *tag = cu__type(cu, tp->type);
struct attributes *tmp;
- if (tag == NULL)
+ if (component_idx >= 0) {
+ struct tag *func_tag = cu__function(cu, tp->type);
+
+ if (func_tag != NULL) {
+ tag = function__parameter(tag__function(func_tag), cu,
+ component_idx);
+ if (tag == NULL) {
+ fprintf(stderr, "WARNING: BTF_KIND_DECL_TAG for unknown parameter %d in BTF id %d\n",
+ component_idx, tp->type);
+ return 0;
+ }
+ }
+ }
+
+ if (tag == NULL && component_idx < 0)
tag = cu__function(cu, tp->type);
if (tag == NULL)
diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c
index 1ec478c2a027..54f483244bd2 100644
--- a/dwarves_fprintf.c
+++ b/dwarves_fprintf.c
@@ -1199,6 +1199,18 @@ const char *function__prototype(const struct function *func,
bf, len);
}
+static size_t tag__attributes_fprintf(const struct tag *tag, FILE *fp)
+{
+ size_t printed = 0;
+ int i;
+
+ if (tag->attributes)
+ for (i = 0; i < tag->attributes->cnt; ++i)
+ printed += fprintf(fp, "%s ", tag->attributes->values[i]);
+
+ return printed;
+}
+
size_t ftype__fprintf_parms(const struct ftype *ftype,
const struct cu *cu, int indent,
const struct conf_fprintf *conf, FILE *fp)
@@ -1240,6 +1252,7 @@ size_t ftype__fprintf_parms(const struct ftype *ftype,
if (n)
return printed + n;
if (ptype->tag == DW_TAG_subroutine_type) {
+ printed += tag__attributes_fprintf(&pos->tag, fp);
printed +=
ftype__fprintf(tag__ftype(ptype),
cu, name, 0, 1, 0,
@@ -1248,12 +1261,14 @@ size_t ftype__fprintf_parms(const struct ftype *ftype,
}
}
} else if (type->tag == DW_TAG_subroutine_type) {
+ printed += tag__attributes_fprintf(&pos->tag, fp);
printed += ftype__fprintf(tag__ftype(type), cu, name,
true, 0, 0, 0, conf, fp);
continue;
}
stype = tag__name(type, cu, sbf, sizeof(sbf), conf);
print_it:
+ printed += tag__attributes_fprintf(&pos->tag, fp);
printed += fprintf(fp, "%s%s%s", stype, name ? " " : "",
name ?: "");
}
@@ -1405,11 +1420,8 @@ static size_t function__fprintf(const struct tag *tag, const struct cu *cu,
struct ftype *ftype = func->btf ? tag__ftype(cu__type(cu, func->proto.tag.type)) : &func->proto;
size_t printed = 0;
bool inlined = !conf->strip_inline && function__declared_inline(func);
- int i;
- if (tag->attributes)
- for (i = 0; i < tag->attributes->cnt; ++i)
- printed += fprintf(fp, "%s ", tag->attributes->values[i]);
+ printed += tag__attributes_fprintf(tag, fp);
if (func->virtuality == DW_VIRTUALITY_virtual ||
func->virtuality == DW_VIRTUALITY_pure_virtual)
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PAHOLE v5 2/5] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC]
2026-06-18 0:57 [PAHOLE v5 0/5] support for DW_TAG_GNU_annotation and fixes Vineet Gupta
2026-06-18 0:57 ` [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters Vineet Gupta
@ 2026-06-18 0:57 ` Vineet Gupta
2026-06-18 0:57 ` [PAHOLE v5 3/5] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
` (2 subsequent siblings)
4 siblings, 0 replies; 11+ messages in thread
From: Vineet Gupta @ 2026-06-18 0:57 UTC (permalink / raw)
To: dwarves
Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
jose.marchesi, David Faust, Yonghong Song, Vineet Gupta
NFC change preparing for DW_TAG_GNU_annotation support.
Extract the btf_type_tag annotation creation logic into helper
die__add_btf_type_tag().
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
---
Changes since v [4]
- Collected the review tags [Emil, Yonghong]
Changes since v3 [3]
- Fix comment to kernel style [Arnaldo]
Changes since v2 [2]
- die__add_btf_type_tag() returns pointer not error code [Alan]
Changes since v1 [1]
- NFC reinstate some original comments
[4] https://lore.kernel.org/bpf/20260602195512.1511013-1-vineet.gupta@linux.dev/
[3] https://lore.kernel.org/bpf/20260601183511.594100-1-vineet.gupta@linux.dev/
[2] https://lore.kernel.org/bpf/20260528223616.2035618-1-vineet.gupta@linux.dev/
[1] https://lore.kernel.org/bpf/20260526181818.4159927-1-vineet.gupta@linux.dev/
---
dwarf_loader.c | 58 +++++++++++++++++++++++++++++---------------------
1 file changed, 34 insertions(+), 24 deletions(-)
diff --git a/dwarf_loader.c b/dwarf_loader.c
index 16fb7becffee..42a0f3f74ce3 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -1600,14 +1600,44 @@ static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *di
return tag;
}
+static struct btf_type_tag_ptr_type *die__add_btf_type_tag(struct btf_type_tag_ptr_type *tag,
+ Dwarf_Die *die, Dwarf_Die *adie,
+ struct cu *cu, struct conf_load *conf)
+{
+ struct btf_type_tag_type *annot;
+ uint32_t id;
+
+ if (tag == NULL) {
+ tag = die__create_new_btf_type_tag_ptr_type(die, cu);
+ if (!tag)
+ return NULL;
+ }
+
+ annot = die__create_new_btf_type_tag_type(adie, cu, conf);
+ if (annot == NULL)
+ return NULL;
+
+ if (cu__table_add_tag(cu, &annot->tag, &id) < 0)
+ return NULL;
+
+ struct dwarf_tag *dtag = tag__dwarf(&annot->tag);
+ dtag->small_id = id;
+ cu__hash(cu, &annot->tag);
+
+ /*
+ * Prepends: for annotations tag1 -> tag2 -> tag3,
+ * the tag->tags list ends up as tag3 -> tag2 -> tag1.
+ */
+ list_add(&annot->node, &tag->tags);
+ return tag;
+}
+
static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu,
struct conf_load *conf)
{
struct btf_type_tag_ptr_type *tag = NULL;
- struct btf_type_tag_type *annot;
Dwarf_Die *cdie, child;
const char *name;
- uint32_t id;
/* If no child tags or skipping btf_type_tag encoding, just create a new tag
* and return
@@ -1627,29 +1657,9 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu,
if (strcmp(name, "btf_type_tag") != 0)
continue;
- if (tag == NULL) {
- /* Create a btf_type_tag_ptr type. */
- tag = die__create_new_btf_type_tag_ptr_type(die, cu);
- if (!tag)
- return NULL;
- }
-
- /* Create a btf_type_tag type for this annotation. */
- annot = die__create_new_btf_type_tag_type(cdie, cu, conf);
- if (annot == NULL)
- return NULL;
-
- if (cu__table_add_tag(cu, &annot->tag, &id) < 0)
+ tag = die__add_btf_type_tag(tag, die, cdie, cu, conf);
+ if (tag == NULL)
return NULL;
-
- struct dwarf_tag *dtag = tag__dwarf(&annot->tag);
- dtag->small_id = id;
- cu__hash(cu, &annot->tag);
-
- /* For a list of DW_TAG_LLVM_annotation like tag1 -> tag2 -> tag3,
- * the tag->tags contains tag3 -> tag2 -> tag1.
- */
- list_add(&annot->node, &tag->tags);
} while (dwarf_siblingof(cdie, cdie) == 0);
return tag ? &tag->tag : tag__new(die, cu);
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PAHOLE v5 3/5] dwarf_loader: Add support for DW_TAG_GNU_annotation
2026-06-18 0:57 [PAHOLE v5 0/5] support for DW_TAG_GNU_annotation and fixes Vineet Gupta
2026-06-18 0:57 ` [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters Vineet Gupta
2026-06-18 0:57 ` [PAHOLE v5 2/5] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta
@ 2026-06-18 0:57 ` Vineet Gupta
2026-06-18 0:57 ` [PAHOLE v5 4/5] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
2026-06-18 0:57 ` [PAHOLE v5 5/5] tests: Add btf_type_tag ordering test Vineet Gupta
4 siblings, 0 replies; 11+ messages in thread
From: Vineet Gupta @ 2026-06-18 0:57 UTC (permalink / raw)
To: dwarves
Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
jose.marchesi, David Faust, Yonghong Song, Vineet Gupta
gcc 16 was the first release to support DW_TAG_GNU_annotations and this
patch enables the same in pahole. Bulk of changes are dwarf_loader but
btf_encoder also gains support with minimal changes.
GCC encodes btf_type_tag and btf_decl_tag annotations differently from
LLVM. While LLVM uses DW_TAG_LLVM_annotation (0x6000) as child DIEs,
GCC uses DW_TAG_GNU_annotation (0x6001) as standalone sibling DIEs
referenced via DW_AT_GNU_annotation (0x2139) attributes, with chaining
through the same attribute on annotation DIEs themselves.
Handle both encoding styles:
For btf_type_tag (pointer annotations):
- Recognize DW_TAG_GNU_annotation alongside DW_TAG_LLVM_annotation in
child annotation scanning.
- Follow DW_AT_GNU_annotation attribute chains on pointer types for
GCC-style btf_type_tag resolution.
- Normalize DW_TAG_GNU_annotation to DW_TAG_LLVM_annotation in the
internal representation so downstream code works unchanged.
- Preserve btf_type_tag emission order across both formats: LLVM child
DIEs come in source order and are prepended (reversed) into the tag
list, while a GCC DW_AT_GNU_annotation chain is already in BTF order
and is appended. die__add_btf_type_tag() takes a 'prepend' argument
to select between the two.
For btf_decl_tag (function/struct/member annotations):
- Add add_gnu_annotation_chain() to follow DW_AT_GNU_annotation
attribute chains on function, struct, and member DIEs.
- GCC puts DW_AT_GNU_annotation on the function/struct DIE itself
(not as child DIEs), referencing sibling annotation DIEs that chain
via the same attribute.
- Follow DW_AT_GNU_annotation chains on formal parameter DIEs so that
GCC-style parameter decl tags are loaded too.
Also:
- Silently skip standalone DW_TAG_GNU_annotation DIEs at CU level.
- Add tag__is_annotation() helper macro for annotation tag checks.
- Rename add_llvm_annotation -> add_tag_annotation,
skip_llvm_annotations -> skip_tag_annotations since these now
handle both LLVM and GNU annotation formats.
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
---
Changes since v4 [4]
- Handle gcc-style function paramter tags [Alan]
- Handle multi tag ordering correctly for LLVM and GCC [Alan]
- Collected the review tags [Emil, Yonghong]
Changes since v3 [3]
- Add helper tag__is_annotation [Emil]
- Rename some functions containing "llvm" as they are common to LLVM/GCC tags [Emil]
- Deduplicate checks before loop and inside loop in add_gnu_annotation_chain() and check_gnu_attr [Arnaldo]
- Fix some typos and move some comments [Emi]
Changes since v2 [2]
- Removed loop detection logic [Alan]
- Move test changes to different patch [Alan]
Changes since v1 [1]
- NFC Reduce indentation with early exits (Alexei offlist)
[4] https://lore.kernel.org/bpf/20260602195512.1511013-2-vineet.gupta@linux.dev/
[3] https://lore.kernel.org/bpf/20260601183511.594100-2-vineet.gupta@linux.dev/
[2] https://lore.kernel.org/bpf/20260528223616.2035618-2-vineet.gupta@linux.dev/
[1] https://lore.kernel.org/bpf/20260526181818.4159927-2-vineet.gupta@linux.dev/
---
btf_encoder.c | 1 +
dutil.h | 11 ++++
dwarf_loader.c | 141 +++++++++++++++++++++++++++++++++++++++-------
dwarves.h | 2 +-
dwarves_fprintf.c | 12 ++--
5 files changed, 141 insertions(+), 26 deletions(-)
diff --git a/btf_encoder.c b/btf_encoder.c
index 633bc6162ce0..d5af706d7638 100644
--- a/btf_encoder.c
+++ b/btf_encoder.c
@@ -1831,6 +1831,7 @@ static int btf_encoder__encode_tag(struct btf_encoder *encoder, struct tag *tag,
name = namespace__name(tag__namespace(tag));
return btf_encoder__add_ref_type(encoder, BTF_KIND_TYPEDEF, ref_type_id, name, false);
case DW_TAG_LLVM_annotation:
+ case DW_TAG_GNU_annotation:
name = tag__btf_type_tag(tag)->value;
return btf_encoder__add_ref_type(encoder, BTF_KIND_TYPE_TAG, ref_type_id, name, false);
case DW_TAG_structure_type:
diff --git a/dutil.h b/dutil.h
index ff78aa6dfd10..be02c68cf4a7 100644
--- a/dutil.h
+++ b/dutil.h
@@ -35,6 +35,17 @@
#define DW_TAG_LLVM_annotation 0x6000
#endif
+#ifndef DW_TAG_GNU_annotation
+#define DW_TAG_GNU_annotation 0x6001
+#endif
+
+#ifndef DW_AT_GNU_annotation
+#define DW_AT_GNU_annotation 0x2139
+#endif
+
+#define tag__is_annotation(tag) \
+ ((tag) == DW_TAG_LLVM_annotation || (tag) == DW_TAG_GNU_annotation)
+
static inline __attribute__((const)) bool is_power_of_2(unsigned long n)
{
return (n != 0 && ((n & (n - 1)) == 0));
diff --git a/dwarf_loader.c b/dwarf_loader.c
index 42a0f3f74ce3..8ce34cbb8ed6 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -908,7 +908,12 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b
return -ENOMEM;
}
-static int add_llvm_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf,
+static bool die__tag_is_annotation(Dwarf_Die *die)
+{
+ return tag__is_annotation(dwarf_tag(die));
+}
+
+static int add_tag_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf,
struct list_head *head)
{
struct llvm_annotation *annot;
@@ -943,16 +948,48 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx,
die = &child;
do {
- if (dwarf_tag(die) == DW_TAG_LLVM_annotation) {
- ret = add_llvm_annotation(die, component_idx, conf, head);
- if (ret)
- return ret;
- }
+ if (!die__tag_is_annotation(die))
+ continue;
+
+ ret = add_tag_annotation(die, component_idx, conf, head);
+ if (ret)
+ return ret;
} while (dwarf_siblingof(die, die) == 0);
return 0;
}
+/*
+ * Handle gcc style btf_decl_tag annotations attached directly to a DIE via
+ * the DW_AT_GNU_annotation attribute chain: functions, function parameters
+ * and struct/union members.
+ *
+ * Note: gcc does not yet emit decl tags for a whole struct/union or for a
+ * typedef, so calling this on such a DIE is harmless - the attribute is
+ * simply absent and the chain is empty.
+ *
+ * Pointers (btf_type_tag) are handled separately, inline in
+ * die__create_new_pointer_tag().
+ */
+static int add_gnu_annotation_chain(Dwarf_Die *die, int component_idx,
+ struct conf_load *conf, struct list_head *head)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Die annot_die;
+
+ while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL &&
+ dwarf_formref_die(&attr, &annot_die) != NULL &&
+ dwarf_tag(&annot_die) == DW_TAG_GNU_annotation) {
+ int ret = add_tag_annotation(&annot_die, component_idx, conf, head);
+ if (ret)
+ return ret;
+
+ die = &annot_die;
+ }
+
+ return 0;
+}
+
int class_member__dwarf_recode_bitfield(struct class_member *member,
struct cu *cu)
{
@@ -1596,13 +1633,16 @@ static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *di
return NULL;
tag__init(&tag->tag, cu, die);
+ /* Normalize DW_TAG_GNU_annotation to DW_TAG_LLVM_annotation internally */
+ tag->tag.tag = DW_TAG_LLVM_annotation;
tag->value = attr_string(die, DW_AT_const_value, conf);
return tag;
}
static struct btf_type_tag_ptr_type *die__add_btf_type_tag(struct btf_type_tag_ptr_type *tag,
Dwarf_Die *die, Dwarf_Die *adie,
- struct cu *cu, struct conf_load *conf)
+ struct cu *cu, struct conf_load *conf,
+ bool prepend)
{
struct btf_type_tag_type *annot;
uint32_t id;
@@ -1625,10 +1665,23 @@ static struct btf_type_tag_ptr_type *die__add_btf_type_tag(struct btf_type_tag_p
cu__hash(cu, &annot->tag);
/*
- * Prepends: for annotations tag1 -> tag2 -> tag3,
- * the tag->tags list ends up as tag3 -> tag2 -> tag1.
+ * Consider:
+ * struct sample __tag(outer) __tag(inner) *global_ptr;
+ * The BTF chain should describe the pointer as:
+ * PTR -> TYPE_TAG "inner" -> TYPE_TAG "outer" -> STRUCT "sample"
+ * i.e. the tag nearest the '*' wraps the pointee first.
+ *
+ * LLVM emits the child annotation DIEs in source order (outer, inner),
+ * so we prepend to end up with inner -> outer.
+ *
+ * GCC's DW_AT_GNU_annotation chain is already walked from the outermost
+ * BTF wrapper (inner) towards the pointee, so we preserve that order by
+ * appending.
*/
- list_add(&annot->node, &tag->tags);
+ if (prepend)
+ list_add(&annot->node, &tag->tags);
+ else
+ list_add_tail(&annot->node, &tag->tags);
return tag;
}
@@ -1637,19 +1690,21 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu,
{
struct btf_type_tag_ptr_type *tag = NULL;
Dwarf_Die *cdie, child;
+ Dwarf_Attribute attr;
+ Dwarf_Die annot_die;
const char *name;
- /* If no child tags or skipping btf_type_tag encoding, just create a new tag
- * and return
- */
- if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0 ||
- conf->skip_encoding_btf_type_tag)
+ /* If skipping btf_type_tag encoding, just create a new tag, return */
+ if (conf->skip_encoding_btf_type_tag)
return tag__new(die, cu);
- /* Otherwise, check DW_TAG_LLVM_annotation child tags */
+ if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0)
+ goto check_gnu_attr;
+
+ /* Handle LLVM style annotation tags if present */
cdie = &child;
do {
- if (dwarf_tag(cdie) != DW_TAG_LLVM_annotation)
+ if (!die__tag_is_annotation(cdie))
continue;
/* Only check btf_type_tag annotations */
@@ -1657,11 +1712,33 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu,
if (strcmp(name, "btf_type_tag") != 0)
continue;
- tag = die__add_btf_type_tag(tag, die, cdie, cu, conf);
+ /* LLVM child DIEs are in source order; prepend to reverse. */
+ tag = die__add_btf_type_tag(tag, die, cdie, cu, conf, true);
if (tag == NULL)
return NULL;
} while (dwarf_siblingof(cdie, cdie) == 0);
+check_gnu_attr:
+ if (tag != NULL)
+ goto out;
+
+ /* Handle GCC-style DW_AT_GNU_annotation attribute */
+ while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL &&
+ dwarf_formref_die(&attr, &annot_die) != NULL &&
+ dwarf_tag(&annot_die) == DW_TAG_GNU_annotation) {
+ name = attr_string(&annot_die, DW_AT_name, conf);
+ if (strcmp(name, "btf_type_tag") != 0)
+ break;
+
+ /* GCC chain is already in BTF order; append to preserve it. */
+ tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf, false);
+ if (tag == NULL)
+ return NULL;
+
+ die = &annot_die;
+ }
+
+out:
return tag ? &tag->tag : tag__new(die, cu);
}
@@ -1690,6 +1767,12 @@ static struct tag *die__create_new_class(Dwarf_Die *die, struct cu *cu, struct c
}
}
+ if (class != NULL &&
+ add_gnu_annotation_chain(die, -1, conf, &class->type.namespace.annots) != 0) {
+ class__delete(class, cu);
+ class = NULL;
+ }
+
return class ? &class->type.namespace.tag : NULL;
}
@@ -1822,6 +1905,8 @@ static struct tag *die__create_new_parameter(Dwarf_Die *die,
if (param_idx >= 0) {
if (add_child_llvm_annotations(die, param_idx, conf, &(tag__function(&ftype->tag)->annots)))
return NULL;
+ if (add_gnu_annotation_chain(die, param_idx, conf, &(tag__function(&ftype->tag)->annots)))
+ return NULL;
}
} else {
/*
@@ -2051,11 +2136,14 @@ static int die__process_class(Dwarf_Die *die, struct type *class,
cu__hash(cu, &member->tag);
if (add_child_llvm_annotations(die, member_idx, conf, &class->namespace.annots))
return -ENOMEM;
+ if (add_gnu_annotation_chain(die, member_idx, conf, &class->namespace.annots))
+ return -ENOMEM;
member_idx++;
}
continue;
case DW_TAG_LLVM_annotation:
- if (add_llvm_annotation(die, -1, conf, &class->namespace.annots))
+ case DW_TAG_GNU_annotation:
+ if (add_tag_annotation(die, -1, conf, &class->namespace.annots))
return -ENOMEM;
continue;
default: {
@@ -2360,7 +2448,8 @@ static int die__process_function(Dwarf_Die *die, struct ftype *ftype,
goto out_enomem;
continue;
case DW_TAG_LLVM_annotation:
- if (add_llvm_annotation(die, -1, conf, &(tag__function(&ftype->tag)->annots)))
+ case DW_TAG_GNU_annotation:
+ if (add_tag_annotation(die, -1, conf, &(tag__function(&ftype->tag)->annots)))
goto out_enomem;
continue;
default:
@@ -2408,6 +2497,12 @@ static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *cu, struc
function = NULL;
}
+ if (function != NULL &&
+ add_gnu_annotation_chain(die, -1, conf, &function->annots) != 0) {
+ function__delete(function, cu);
+ function = NULL;
+ }
+
return function ? &function->proto.tag : NULL;
}
@@ -2469,6 +2564,9 @@ static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu,
*/
tag = &unsupported_tag;
break;
+ case DW_TAG_GNU_annotation:
+ tag = &unsupported_tag;
+ break;
case DW_TAG_label:
if (conf->ignore_labels)
tag = &unsupported_tag; // callers will assume conf->ignore_labels is true
@@ -2494,7 +2592,8 @@ static int die__process_unit(Dwarf_Die *die, struct cu *cu, struct conf_load *co
// XXX special case DW_TAG_dwarf_procedure, appears when looking at a recent ~/bin/perf
// Investigate later how to properly support this...
if (dwarf_tag(die) != DW_TAG_dwarf_procedure &&
- dwarf_tag(die) != DW_TAG_label) // conf->ignore_labels == true, see die__process_tag()
+ dwarf_tag(die) != DW_TAG_label && // conf->ignore_labels == true, see die__process_tag()
+ dwarf_tag(die) != DW_TAG_GNU_annotation)
tag__print_not_supported(die);
continue;
}
diff --git a/dwarves.h b/dwarves.h
index 5ec16e750e83..75c311a2f8ee 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -670,7 +670,7 @@ static inline int tag__is_tag_type(const struct tag *tag)
tag->tag == DW_TAG_volatile_type ||
tag->tag == DW_TAG_atomic_type ||
tag->tag == DW_TAG_unspecified_type ||
- tag->tag == DW_TAG_LLVM_annotation;
+ tag__is_annotation(tag->tag);
}
static inline const char *tag__decl_file(const struct tag *tag,
diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c
index 54f483244bd2..ab1c381db646 100644
--- a/dwarves_fprintf.c
+++ b/dwarves_fprintf.c
@@ -140,6 +140,8 @@ const char *dwarf_tag_name(const uint32_t tag)
return dwarf_gnu_tag_names[tag - DW_TAG_MIPS_loop];
else if (tag == DW_TAG_LLVM_annotation)
return "LLVM_annotation";
+ else if (tag == DW_TAG_GNU_annotation)
+ return "GNU_annotation";
return "INVALID";
}
@@ -658,6 +660,7 @@ static const char *__tag__name(const struct tag *tag, const struct cu *cu,
snprintf(bf, len, "%s", variable__name(tag__variable(tag)));
break;
case DW_TAG_LLVM_annotation:
+ case DW_TAG_GNU_annotation:
type = cu__type(cu, tag->type);
if (type == NULL && tag->type != 0)
tag__id_not_found_snprintf(bf, len, tag->type);
@@ -723,7 +726,7 @@ static size_t type__fprintf_stats(struct type *type, const struct cu *cu,
return printed;
}
-static type_id_t skip_llvm_annotations(const struct cu *cu, type_id_t id)
+static type_id_t skip_tag_annotations(const struct cu *cu, type_id_t id)
{
struct tag *type;
@@ -731,7 +734,7 @@ static type_id_t skip_llvm_annotations(const struct cu *cu, type_id_t id)
if (id == 0)
break;
type = cu__type(cu, id);
- if (type == NULL || type->tag != DW_TAG_LLVM_annotation || type->type == id)
+ if (type == NULL || !tag__is_annotation(type->tag) || type->type == id)
break;
id = type->type;
}
@@ -838,7 +841,7 @@ inner_struct:
next_type:
switch (type->tag) {
case DW_TAG_pointer_type: {
- type_id_t ptype_id = skip_llvm_annotations(cu, type->type);
+ type_id_t ptype_id = skip_tag_annotations(cu, type->type);
if (ptype_id != 0) {
int n;
@@ -936,7 +939,8 @@ print_modifier: {
else
printed += enumeration__fprintf(type, &tconf, fp);
break;
- case DW_TAG_LLVM_annotation: {
+ case DW_TAG_LLVM_annotation:
+ case DW_TAG_GNU_annotation: {
struct tag *ttype = cu__type(cu, type->type);
if (ttype) {
type = ttype;
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PAHOLE v5 4/5] tests: Support GCC in pfunct-btf-decl-tags test
2026-06-18 0:57 [PAHOLE v5 0/5] support for DW_TAG_GNU_annotation and fixes Vineet Gupta
` (2 preceding siblings ...)
2026-06-18 0:57 ` [PAHOLE v5 3/5] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
@ 2026-06-18 0:57 ` Vineet Gupta
2026-06-18 0:57 ` [PAHOLE v5 5/5] tests: Add btf_type_tag ordering test Vineet Gupta
4 siblings, 0 replies; 11+ messages in thread
From: Vineet Gupta @ 2026-06-18 0:57 UTC (permalink / raw)
To: dwarves
Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
jose.marchesi, David Faust, Yonghong Song, Vineet Gupta
GCC 16+ supports btf_decl_tag via DW_TAG_GNU_annotation. Update the
test to run with both GCC (>= 16) and clang when available, instead
of requiring clang only.
Compile the clang case natively and encode BTF with "pahole -J" (like
the GCC case) rather than emitting BPF-target BTF directly, so both
compilers exercise the same DWARF -> BTF path.
Add a function with a parameter decl tag (qux) to cover decl tags that
carry a component_idx, and make the awk post-processing pass through
lines that have no leading tags.
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
---
Changes since v4 [4]
- Test function param decl tag [Alan]
- Missing "pahole -j" for clang [Yonghong]
- Collected the review tags [Emil, Yonghong]
[4] https://lore.kernel.org/bpf/20260602195512.1511013-3-vineet.gupta@linux.dev/
---
tests/pfunct-btf-decl-tags.sh | 93 ++++++++++++++++++++++++++---------
1 file changed, 70 insertions(+), 23 deletions(-)
diff --git a/tests/pfunct-btf-decl-tags.sh b/tests/pfunct-btf-decl-tags.sh
index 35884b4e8687..520148b2ccef 100755
--- a/tests/pfunct-btf-decl-tags.sh
+++ b/tests/pfunct-btf-decl-tags.sh
@@ -6,43 +6,60 @@
source test_lib.sh
outdir=$(make_tmpdir)
-tmpobj=$(make_tmpobj)
# Comment this out to save test data.
trap cleanup EXIT
title_log "Check that pfunct can print btf_decl_tags read from BTF."
-# gcc now also supports decl tags as of gcc commit 43dcea48b8c,
-# in upstream version 16.
-# UPTODO: add a check here for that.
+# gcc 16+ supports decl tags via DW_TAG_GNU_annotation (gcc commit ac7027f180b).
+GCC=${GCC:-gcc}
CLANG=${CLANG:-clang}
-if ! command -v $CLANG > /dev/null; then
- error_log "Need clang for test $0"
+
+use_gcc=0
+if command -v $GCC > /dev/null; then
+ gcc_ver=$($GCC -dumpversion 2>/dev/null | cut -d. -f1)
+ if [ "$gcc_ver" -ge 16 ] 2>/dev/null; then
+ use_gcc=1
+ fi
+fi
+
+use_clang=0
+if command -v $CLANG > /dev/null; then
+ use_clang=1
+fi
+
+if [ "$use_gcc" -eq 0 ] && [ "$use_clang" -eq 0 ]; then
+ error_log "Need gcc >= 16 or clang for test $0"
test_fail
fi
-(cat <<EOF
+src=$(cat <<EOF
#define __tag(x) __attribute__((btf_decl_tag(#x)))
__tag(a) __tag(b) __tag(c) void foo(void) {}
__tag(a) __tag(b) void bar(void) {}
__tag(a) void buz(void) {}
+void qux(int __tag(param_a) arg) {}
EOF
-) | $CLANG --target=bpf -c -g -x c -o $tmpobj -
+)
# tags order is not guaranteed
sort_tags=$(cat <<EOF
{
-match(\$0,/^(.*) (void .*)/,tags_and_proto);
-tags = tags_and_proto[1];
-proto = tags_and_proto[2];
-split(tags, tags_arr ,/ /);
-asort(tags_arr);
-for (t in tags_arr) printf "%s ", tags_arr[t];
-print proto;
+delete tags_arr;
+if (match(\$0,/^(.*) (void .*)/,tags_and_proto)) {
+ tags = tags_and_proto[1];
+ proto = tags_and_proto[2];
+ split(tags, tags_arr ,/ /);
+ asort(tags_arr);
+ for (t in tags_arr) printf "%s ", tags_arr[t];
+ print proto;
+} else {
+ print \$0;
+}
}
EOF
)
@@ -51,19 +68,49 @@ expected=$(cat <<EOF
a b c void foo(void);
a b void bar(void);
a void buz(void);
+void qux(param_a int arg);
EOF
)
-out=$(pfunct -P -F btf $tmpobj | awk "$sort_tags" | sort)
-d=$(diff -u <(echo "$expected") <(echo "$out"))
+run_test() {
+ local compiler=$1
+ local tmpobj=$2
+
+ info_log "Testing with $compiler"
+ out=$(pfunct -P -F btf $tmpobj | awk "$sort_tags" | sort)
+ d=$(diff -u <(echo "$expected") <(echo "$out"))
+
+ if [[ "$d" == "" ]]; then
+ info_log " passed"
+ return 0
+ else
+ error_log "pfunct output does not match expected ($compiler):"
+ info_log "$d"
+ info_log
+ info_log "Complete output:"
+ info_log "$out"
+ return 1
+ fi
+}
+
+failed=0
+
+if [ "$use_gcc" -eq 1 ]; then
+ tmpobj=$(make_tmpobj)
+ echo "$src" | $GCC -c -g -x c -o $tmpobj - 2>/dev/null
+ pahole -J $tmpobj 2>/dev/null
+ run_test "$GCC (version $gcc_ver)" "$tmpobj" || failed=1
+fi
+
+if [ "$use_clang" -eq 1 ]; then
+ tmpobj=$(make_tmpobj)
+ echo "$src" | $CLANG -c -g -x c -o $tmpobj -
+ pahole -J $tmpobj 2>/dev/null
+ run_test "$CLANG" "$tmpobj" || failed=1
+fi
-if [[ "$d" == "" ]]; then
+if [ "$failed" -eq 0 ]; then
test_pass
else
- error_log "pfunct output does not match expected:"
- info_log "$d"
- info_log
- info_log "Complete output:"
- info_log "$out"
test_fail
fi
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PAHOLE v5 5/5] tests: Add btf_type_tag ordering test
2026-06-18 0:57 [PAHOLE v5 0/5] support for DW_TAG_GNU_annotation and fixes Vineet Gupta
` (3 preceding siblings ...)
2026-06-18 0:57 ` [PAHOLE v5 4/5] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
@ 2026-06-18 0:57 ` Vineet Gupta
2026-06-18 4:47 ` Emil Tsalapatis
4 siblings, 1 reply; 11+ messages in thread
From: Vineet Gupta @ 2026-06-18 0:57 UTC (permalink / raw)
To: dwarves
Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
jose.marchesi, David Faust, Yonghong Song, Vineet Gupta
From: Alan Maguire <alan.maguire@oracle.com>
Add a test that compiles a pointer carrying two btf_type_tags
(__tag(outer) __tag(inner)) and checks the BTF that pahole emits to
ensure the tag chain is PTR -> TYPE_TAG inner -> TYPE_TAG outer ->
struct sample, i.e. the innermost tag is closest to the pointer.
This exercises the type_tag ordering for both encodings: LLVM emits the
annotations as child DIEs in source order (reversed when building the
tag list), while GCC chains them via DW_AT_GNU_annotation already in BTF
order. The test runs with gcc and clang when they support btf_type_tag,
and is skipped when bpftool is unavailable.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
tests/btf_type_tag_order.sh | 179 ++++++++++++++++++++++++++++++++++++
1 file changed, 179 insertions(+)
create mode 100755 tests/btf_type_tag_order.sh
diff --git a/tests/btf_type_tag_order.sh b/tests/btf_type_tag_order.sh
new file mode 100755
index 000000000000..09d1ac346000
--- /dev/null
+++ b/tests/btf_type_tag_order.sh
@@ -0,0 +1,179 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-only
+
+# Check that pahole preserves btf_type_tag order when emitting BTF from DWARF.
+
+source test_lib.sh
+
+outdir=$(make_tmpdir)
+
+# Comment this out to save test data.
+trap cleanup EXIT
+
+title_log "Check BTF type tag order."
+
+GCC=${GCC:-gcc}
+CLANG=${CLANG:-clang}
+PAHOLE=${PAHOLE:-pahole}
+BPFTOOL=${BPFTOOL:-bpftool}
+
+if ! command -v "$BPFTOOL" > /dev/null; then
+ info_log "skip: bpftool not available"
+ test_skip
+fi
+
+compiler_has_btf_type_tag()
+{
+ local compiler=$1
+
+ if ! command -v "$compiler" > /dev/null; then
+ return 1
+ fi
+
+ "$compiler" -x c -E -P - <<'EOF' 2>/dev/null | grep -qx 1
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#endif
+#if __has_attribute(btf_type_tag)
+1
+#else
+0
+#endif
+EOF
+}
+
+use_gcc=0
+if compiler_has_btf_type_tag "$GCC"; then
+ use_gcc=1
+fi
+
+use_clang=0
+if compiler_has_btf_type_tag "$CLANG"; then
+ use_clang=1
+fi
+
+if [ "$use_gcc" -eq 0 ] && [ "$use_clang" -eq 0 ]; then
+ error_log "Need gcc or clang with btf_type_tag support for test $0"
+ test_fail
+fi
+
+src=$(cat <<EOF
+#define __tag(x) __attribute__((btf_type_tag(#x)))
+
+struct sample {
+ int value;
+};
+
+struct sample __tag(outer) __tag(inner) *global_ptr;
+
+EOF
+)
+
+check_type_tag_order()
+{
+ local btf=$1
+ local dump
+
+ if ! dump=$("$BPFTOOL" btf dump file "$btf"); then
+ return 1
+ fi
+
+ printf '%s\n' "$dump" | awk '
+ function parse_id(line, m) {
+ if (match(line, /^\[([0-9]+)\]/, m))
+ return m[1]
+ return 0
+ }
+ function parse_name(line, m) {
+ if (match(line, /\047([^\047]*)\047/, m))
+ return m[1]
+ return ""
+ }
+ function parse_type(line, m) {
+ if (match(line, /type_id=([0-9]+)/, m))
+ return m[1]
+ return 0
+ }
+ function check_ptr(ptr, id, tags, seen) {
+ id = type[ptr]
+ while (id != 0 && !seen[id]) {
+ seen[id] = 1
+ if (kind[id] == "TYPE_TAG") {
+ tags = tags (tags == "" ? "" : " -> ") name[id]
+ id = type[id]
+ continue
+ }
+ if (kind[id] == "STRUCT" && name[id] == "sample") {
+ if (tags == "inner -> outer")
+ exit 0
+ candidates = candidates (candidates == "" ? "" : ", ") tags
+ }
+ return
+ }
+ }
+ /^\[[0-9]+\]/ {
+ id = parse_id($0)
+ kind[id] = $2
+ name[id] = parse_name($0)
+ type[id] = parse_type($0)
+ if (kind[id] == "PTR")
+ ptrs[++nr_ptrs] = id
+ }
+ END {
+ for (i = 1; i <= nr_ptrs; i++)
+ check_ptr(ptrs[i])
+ if (candidates != "") {
+ print "type tag order mismatch; expected inner -> outer, found " candidates > "/dev/stderr"
+ exit 1
+ }
+ print "could not find tagged pointer to struct sample" > "/dev/stderr"
+ exit 1
+ }'
+}
+
+run_test()
+{
+ local compiler=$1
+ local tmpobj=$2
+ local btf=$3
+
+ info_log "Testing with $compiler"
+
+ if ! echo "$src" | "$compiler" -g -c -x c -o "$tmpobj" - 2>/dev/null; then
+ error_log "Could not compile type tag order test with $compiler"
+ return 1
+ fi
+
+ if ! "$PAHOLE" --btf_features=+type_tag --btf_encode_detached="$btf" "$tmpobj" 2>/dev/null; then
+ error_log "Could not encode BTF for $tmpobj"
+ return 1
+ fi
+
+ if check_type_tag_order "$btf"; then
+ info_log " passed"
+ return 0
+ fi
+
+ error_log "BTF type tag order does not match expected order ($compiler)"
+ return 1
+}
+
+failed=0
+
+if [ "$use_gcc" -eq 1 ]; then
+ tmpobj=$(make_tmpobj)
+ btf=${tmpobj%.o}.btf
+ run_test "$GCC" "$tmpobj" "$btf" || failed=1
+fi
+
+if [ "$use_clang" -eq 1 ]; then
+ tmpobj=$(make_tmpobj)
+ btf=${tmpobj%.o}.btf
+ run_test "$CLANG" "$tmpobj" "$btf" || failed=1
+fi
+
+if [ "$failed" -eq 0 ]; then
+ test_pass
+else
+ test_fail
+fi
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters
2026-06-18 0:57 ` [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters Vineet Gupta
@ 2026-06-18 4:46 ` Emil Tsalapatis
2026-06-18 13:27 ` Arnaldo Carvalho de Melo
2026-06-18 14:38 ` Alan Maguire
0 siblings, 2 replies; 11+ messages in thread
From: Emil Tsalapatis @ 2026-06-18 4:46 UTC (permalink / raw)
To: Vineet Gupta, dwarves
Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
jose.marchesi, David Faust, Yonghong Song
On Wed Jun 17, 2026 at 8:57 PM EDT, Vineet Gupta wrote:
> From: Alan Maguire <alan.maguire@oracle.com>
>
> A BTF_KIND_DECL_TAG with a non-negative component_idx applies to a
> specific function parameter (or struct/union member), not to the
> function itself. btf_loader.c however attached every decl tag to the
> type named by btf_type->type, so parameter decl tags were recorded on
> the function rather than on the parameter, and pfunct never printed
> them with the parameter.
>
> Resolve a non-negative component_idx to the corresponding parameter via
> new helpers ftype__parameter()/function__parameter(), and attach the
> tag there. Teach the pretty printer to emit a parameter's attributes by
> factoring the function-level attribute loop into tag__attributes_fprintf()
> and calling it from ftype__fprintf_parms() for each parameter.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
With one nit:
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
(Also feel free to keep the tags for the other 3 patches since the
changes are minimal).
</SNIP>
> + if (component_idx >= 0) {
> + struct tag *func_tag = cu__function(cu, tp->type);
> +
> + if (func_tag != NULL) {
> + tag = function__parameter(tag__function(func_tag), cu,
> + component_idx);
> + if (tag == NULL) {
> + fprintf(stderr, "WARNING: BTF_KIND_DECL_TAG for unknown parameter %d in BTF id %d\n",
> + component_idx, tp->type);
> + return 0;
> + }
> + }
> + }
> +
> + if (tag == NULL && component_idx < 0)
> tag = cu__function(cu, tp->type);
Nit, I think this can be simplified as follows:
tag = cu__function(cu, tp->type);
if (component_idx >= 0 && tag != NULL) {
tag = function__parameter(tag__function(tag), cu, component_idx);
if (tag == NULL) {
fprintf(stderr, "WARNING: BTF_KIND_DECL_TAG for unknown parameter %d in BTF id %d\n",
component_idx, tp->type);
return 0;
}
}
<SNIP>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PAHOLE v5 5/5] tests: Add btf_type_tag ordering test
2026-06-18 0:57 ` [PAHOLE v5 5/5] tests: Add btf_type_tag ordering test Vineet Gupta
@ 2026-06-18 4:47 ` Emil Tsalapatis
2026-06-18 13:25 ` Arnaldo Carvalho de Melo
0 siblings, 1 reply; 11+ messages in thread
From: Emil Tsalapatis @ 2026-06-18 4:47 UTC (permalink / raw)
To: Vineet Gupta, dwarves
Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
jose.marchesi, David Faust, Yonghong Song
On Wed Jun 17, 2026 at 8:57 PM EDT, Vineet Gupta wrote:
> From: Alan Maguire <alan.maguire@oracle.com>
>
> Add a test that compiles a pointer carrying two btf_type_tags
> (__tag(outer) __tag(inner)) and checks the BTF that pahole emits to
> ensure the tag chain is PTR -> TYPE_TAG inner -> TYPE_TAG outer ->
> struct sample, i.e. the innermost tag is closest to the pointer.
>
> This exercises the type_tag ordering for both encodings: LLVM emits the
> annotations as child DIEs in source order (reversed when building the
> tag list), while GCC chains them via DW_AT_GNU_annotation already in BTF
> order. The test runs with gcc and clang when they support btf_type_tag,
> and is skipped when bpftool is unavailable.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
> ---
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
> tests/btf_type_tag_order.sh | 179 ++++++++++++++++++++++++++++++++++++
> 1 file changed, 179 insertions(+)
> create mode 100755 tests/btf_type_tag_order.sh
>
> diff --git a/tests/btf_type_tag_order.sh b/tests/btf_type_tag_order.sh
> new file mode 100755
> index 000000000000..09d1ac346000
> --- /dev/null
> +++ b/tests/btf_type_tag_order.sh
> @@ -0,0 +1,179 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +# Check that pahole preserves btf_type_tag order when emitting BTF from DWARF.
> +
> +source test_lib.sh
> +
> +outdir=$(make_tmpdir)
> +
> +# Comment this out to save test data.
> +trap cleanup EXIT
> +
> +title_log "Check BTF type tag order."
> +
> +GCC=${GCC:-gcc}
> +CLANG=${CLANG:-clang}
> +PAHOLE=${PAHOLE:-pahole}
> +BPFTOOL=${BPFTOOL:-bpftool}
> +
> +if ! command -v "$BPFTOOL" > /dev/null; then
> + info_log "skip: bpftool not available"
> + test_skip
> +fi
> +
> +compiler_has_btf_type_tag()
> +{
> + local compiler=$1
> +
> + if ! command -v "$compiler" > /dev/null; then
> + return 1
> + fi
> +
> + "$compiler" -x c -E -P - <<'EOF' 2>/dev/null | grep -qx 1
> +#ifndef __has_attribute
> +#define __has_attribute(x) 0
> +#endif
> +#if __has_attribute(btf_type_tag)
> +1
> +#else
> +0
> +#endif
> +EOF
> +}
> +
> +use_gcc=0
> +if compiler_has_btf_type_tag "$GCC"; then
> + use_gcc=1
> +fi
> +
> +use_clang=0
> +if compiler_has_btf_type_tag "$CLANG"; then
> + use_clang=1
> +fi
> +
> +if [ "$use_gcc" -eq 0 ] && [ "$use_clang" -eq 0 ]; then
> + error_log "Need gcc or clang with btf_type_tag support for test $0"
> + test_fail
> +fi
> +
> +src=$(cat <<EOF
> +#define __tag(x) __attribute__((btf_type_tag(#x)))
> +
> +struct sample {
> + int value;
> +};
> +
> +struct sample __tag(outer) __tag(inner) *global_ptr;
> +
> +EOF
> +)
> +
> +check_type_tag_order()
> +{
> + local btf=$1
> + local dump
> +
> + if ! dump=$("$BPFTOOL" btf dump file "$btf"); then
> + return 1
> + fi
> +
> + printf '%s\n' "$dump" | awk '
> + function parse_id(line, m) {
> + if (match(line, /^\[([0-9]+)\]/, m))
> + return m[1]
> + return 0
> + }
> + function parse_name(line, m) {
> + if (match(line, /\047([^\047]*)\047/, m))
> + return m[1]
> + return ""
> + }
> + function parse_type(line, m) {
> + if (match(line, /type_id=([0-9]+)/, m))
> + return m[1]
> + return 0
> + }
> + function check_ptr(ptr, id, tags, seen) {
> + id = type[ptr]
> + while (id != 0 && !seen[id]) {
> + seen[id] = 1
> + if (kind[id] == "TYPE_TAG") {
> + tags = tags (tags == "" ? "" : " -> ") name[id]
> + id = type[id]
> + continue
> + }
> + if (kind[id] == "STRUCT" && name[id] == "sample") {
> + if (tags == "inner -> outer")
> + exit 0
> + candidates = candidates (candidates == "" ? "" : ", ") tags
> + }
> + return
> + }
> + }
> + /^\[[0-9]+\]/ {
> + id = parse_id($0)
> + kind[id] = $2
> + name[id] = parse_name($0)
> + type[id] = parse_type($0)
> + if (kind[id] == "PTR")
> + ptrs[++nr_ptrs] = id
> + }
> + END {
> + for (i = 1; i <= nr_ptrs; i++)
> + check_ptr(ptrs[i])
> + if (candidates != "") {
> + print "type tag order mismatch; expected inner -> outer, found " candidates > "/dev/stderr"
> + exit 1
> + }
> + print "could not find tagged pointer to struct sample" > "/dev/stderr"
> + exit 1
> + }'
> +}
> +
> +run_test()
> +{
> + local compiler=$1
> + local tmpobj=$2
> + local btf=$3
> +
> + info_log "Testing with $compiler"
> +
> + if ! echo "$src" | "$compiler" -g -c -x c -o "$tmpobj" - 2>/dev/null; then
> + error_log "Could not compile type tag order test with $compiler"
> + return 1
> + fi
> +
> + if ! "$PAHOLE" --btf_features=+type_tag --btf_encode_detached="$btf" "$tmpobj" 2>/dev/null; then
> + error_log "Could not encode BTF for $tmpobj"
> + return 1
> + fi
> +
> + if check_type_tag_order "$btf"; then
> + info_log " passed"
> + return 0
> + fi
> +
> + error_log "BTF type tag order does not match expected order ($compiler)"
> + return 1
> +}
> +
> +failed=0
> +
> +if [ "$use_gcc" -eq 1 ]; then
> + tmpobj=$(make_tmpobj)
> + btf=${tmpobj%.o}.btf
> + run_test "$GCC" "$tmpobj" "$btf" || failed=1
> +fi
> +
> +if [ "$use_clang" -eq 1 ]; then
> + tmpobj=$(make_tmpobj)
> + btf=${tmpobj%.o}.btf
> + run_test "$CLANG" "$tmpobj" "$btf" || failed=1
> +fi
> +
> +if [ "$failed" -eq 0 ]; then
> + test_pass
> +else
> + test_fail
> +fi
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PAHOLE v5 5/5] tests: Add btf_type_tag ordering test
2026-06-18 4:47 ` Emil Tsalapatis
@ 2026-06-18 13:25 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 11+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-06-18 13:25 UTC (permalink / raw)
To: Emil Tsalapatis
Cc: Vineet Gupta, dwarves, bpf, Andrii Nakryiko, Alan Maguire,
jose.marchesi, David Faust, Yonghong Song
On Thu, Jun 18, 2026 at 12:47:30AM -0400, Emil Tsalapatis wrote:
> On Wed Jun 17, 2026 at 8:57 PM EDT, Vineet Gupta wrote:
> > From: Alan Maguire <alan.maguire@oracle.com>
> >
> > Add a test that compiles a pointer carrying two btf_type_tags
> > (__tag(outer) __tag(inner)) and checks the BTF that pahole emits to
> > ensure the tag chain is PTR -> TYPE_TAG inner -> TYPE_TAG outer ->
> > struct sample, i.e. the innermost tag is closest to the pointer.
> >
> > This exercises the type_tag ordering for both encodings: LLVM emits the
> > annotations as child DIEs in source order (reversed when building the
> > tag list), while GCC chains them via DW_AT_GNU_annotation already in BTF
> > order. The test runs with gcc and clang when they support btf_type_tag,
> > and is skipped when bpftool is unavailable.
> >
> > Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> > Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
> > ---
>
> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com>
- Arnaldo
> > tests/btf_type_tag_order.sh | 179 ++++++++++++++++++++++++++++++++++++
> > 1 file changed, 179 insertions(+)
> > create mode 100755 tests/btf_type_tag_order.sh
> >
> > diff --git a/tests/btf_type_tag_order.sh b/tests/btf_type_tag_order.sh
> > new file mode 100755
> > index 000000000000..09d1ac346000
> > --- /dev/null
> > +++ b/tests/btf_type_tag_order.sh
> > @@ -0,0 +1,179 @@
> > +#!/bin/bash
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +
> > +# Check that pahole preserves btf_type_tag order when emitting BTF from DWARF.
> > +
> > +source test_lib.sh
> > +
> > +outdir=$(make_tmpdir)
> > +
> > +# Comment this out to save test data.
> > +trap cleanup EXIT
> > +
> > +title_log "Check BTF type tag order."
> > +
> > +GCC=${GCC:-gcc}
> > +CLANG=${CLANG:-clang}
> > +PAHOLE=${PAHOLE:-pahole}
> > +BPFTOOL=${BPFTOOL:-bpftool}
> > +
> > +if ! command -v "$BPFTOOL" > /dev/null; then
> > + info_log "skip: bpftool not available"
> > + test_skip
> > +fi
> > +
> > +compiler_has_btf_type_tag()
> > +{
> > + local compiler=$1
> > +
> > + if ! command -v "$compiler" > /dev/null; then
> > + return 1
> > + fi
> > +
> > + "$compiler" -x c -E -P - <<'EOF' 2>/dev/null | grep -qx 1
> > +#ifndef __has_attribute
> > +#define __has_attribute(x) 0
> > +#endif
> > +#if __has_attribute(btf_type_tag)
> > +1
> > +#else
> > +0
> > +#endif
> > +EOF
> > +}
> > +
> > +use_gcc=0
> > +if compiler_has_btf_type_tag "$GCC"; then
> > + use_gcc=1
> > +fi
> > +
> > +use_clang=0
> > +if compiler_has_btf_type_tag "$CLANG"; then
> > + use_clang=1
> > +fi
> > +
> > +if [ "$use_gcc" -eq 0 ] && [ "$use_clang" -eq 0 ]; then
> > + error_log "Need gcc or clang with btf_type_tag support for test $0"
> > + test_fail
> > +fi
> > +
> > +src=$(cat <<EOF
> > +#define __tag(x) __attribute__((btf_type_tag(#x)))
> > +
> > +struct sample {
> > + int value;
> > +};
> > +
> > +struct sample __tag(outer) __tag(inner) *global_ptr;
> > +
> > +EOF
> > +)
> > +
> > +check_type_tag_order()
> > +{
> > + local btf=$1
> > + local dump
> > +
> > + if ! dump=$("$BPFTOOL" btf dump file "$btf"); then
> > + return 1
> > + fi
> > +
> > + printf '%s\n' "$dump" | awk '
> > + function parse_id(line, m) {
> > + if (match(line, /^\[([0-9]+)\]/, m))
> > + return m[1]
> > + return 0
> > + }
> > + function parse_name(line, m) {
> > + if (match(line, /\047([^\047]*)\047/, m))
> > + return m[1]
> > + return ""
> > + }
> > + function parse_type(line, m) {
> > + if (match(line, /type_id=([0-9]+)/, m))
> > + return m[1]
> > + return 0
> > + }
> > + function check_ptr(ptr, id, tags, seen) {
> > + id = type[ptr]
> > + while (id != 0 && !seen[id]) {
> > + seen[id] = 1
> > + if (kind[id] == "TYPE_TAG") {
> > + tags = tags (tags == "" ? "" : " -> ") name[id]
> > + id = type[id]
> > + continue
> > + }
> > + if (kind[id] == "STRUCT" && name[id] == "sample") {
> > + if (tags == "inner -> outer")
> > + exit 0
> > + candidates = candidates (candidates == "" ? "" : ", ") tags
> > + }
> > + return
> > + }
> > + }
> > + /^\[[0-9]+\]/ {
> > + id = parse_id($0)
> > + kind[id] = $2
> > + name[id] = parse_name($0)
> > + type[id] = parse_type($0)
> > + if (kind[id] == "PTR")
> > + ptrs[++nr_ptrs] = id
> > + }
> > + END {
> > + for (i = 1; i <= nr_ptrs; i++)
> > + check_ptr(ptrs[i])
> > + if (candidates != "") {
> > + print "type tag order mismatch; expected inner -> outer, found " candidates > "/dev/stderr"
> > + exit 1
> > + }
> > + print "could not find tagged pointer to struct sample" > "/dev/stderr"
> > + exit 1
> > + }'
> > +}
> > +
> > +run_test()
> > +{
> > + local compiler=$1
> > + local tmpobj=$2
> > + local btf=$3
> > +
> > + info_log "Testing with $compiler"
> > +
> > + if ! echo "$src" | "$compiler" -g -c -x c -o "$tmpobj" - 2>/dev/null; then
> > + error_log "Could not compile type tag order test with $compiler"
> > + return 1
> > + fi
> > +
> > + if ! "$PAHOLE" --btf_features=+type_tag --btf_encode_detached="$btf" "$tmpobj" 2>/dev/null; then
> > + error_log "Could not encode BTF for $tmpobj"
> > + return 1
> > + fi
> > +
> > + if check_type_tag_order "$btf"; then
> > + info_log " passed"
> > + return 0
> > + fi
> > +
> > + error_log "BTF type tag order does not match expected order ($compiler)"
> > + return 1
> > +}
> > +
> > +failed=0
> > +
> > +if [ "$use_gcc" -eq 1 ]; then
> > + tmpobj=$(make_tmpobj)
> > + btf=${tmpobj%.o}.btf
> > + run_test "$GCC" "$tmpobj" "$btf" || failed=1
> > +fi
> > +
> > +if [ "$use_clang" -eq 1 ]; then
> > + tmpobj=$(make_tmpobj)
> > + btf=${tmpobj%.o}.btf
> > + run_test "$CLANG" "$tmpobj" "$btf" || failed=1
> > +fi
> > +
> > +if [ "$failed" -eq 0 ]; then
> > + test_pass
> > +else
> > + test_fail
> > +fi
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters
2026-06-18 4:46 ` Emil Tsalapatis
@ 2026-06-18 13:27 ` Arnaldo Carvalho de Melo
2026-06-18 14:38 ` Alan Maguire
1 sibling, 0 replies; 11+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-06-18 13:27 UTC (permalink / raw)
To: Emil Tsalapatis
Cc: Vineet Gupta, dwarves, bpf, Andrii Nakryiko, Alan Maguire,
jose.marchesi, David Faust, Yonghong Song
On Thu, Jun 18, 2026 at 12:46:50AM -0400, Emil Tsalapatis wrote:
> On Wed Jun 17, 2026 at 8:57 PM EDT, Vineet Gupta wrote:
> > From: Alan Maguire <alan.maguire@oracle.com>
> >
> > A BTF_KIND_DECL_TAG with a non-negative component_idx applies to a
> > specific function parameter (or struct/union member), not to the
> > function itself. btf_loader.c however attached every decl tag to the
> > type named by btf_type->type, so parameter decl tags were recorded on
> > the function rather than on the parameter, and pfunct never printed
> > them with the parameter.
> >
> > Resolve a non-negative component_idx to the corresponding parameter via
> > new helpers ftype__parameter()/function__parameter(), and attach the
> > tag there. Teach the pretty printer to emit a parameter's attributes by
> > factoring the function-level attribute loop into tag__attributes_fprintf()
> > and calling it from ftype__fprintf_parms() for each parameter.
> >
> > Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> > Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
>
> With one nit:
>
> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
>
> (Also feel free to keep the tags for the other 3 patches since the
> changes are minimal).
>
> </SNIP>
>
> > + if (component_idx >= 0) {
> > + struct tag *func_tag = cu__function(cu, tp->type);
> > +
> > + if (func_tag != NULL) {
> > + tag = function__parameter(tag__function(func_tag), cu,
> > + component_idx);
> > + if (tag == NULL) {
> > + fprintf(stderr, "WARNING: BTF_KIND_DECL_TAG for unknown parameter %d in BTF id %d\n",
> > + component_idx, tp->type);
> > + return 0;
> > + }
> > + }
> > + }
> > +
> > + if (tag == NULL && component_idx < 0)
> > tag = cu__function(cu, tp->type);
>
> Nit, I think this can be simplified as follows:
>
> tag = cu__function(cu, tp->type);
> if (component_idx >= 0 && tag != NULL) {
> tag = function__parameter(tag__function(tag), cu, component_idx);
> if (tag == NULL) {
> fprintf(stderr, "WARNING: BTF_KIND_DECL_TAG for unknown parameter %d in BTF id %d\n",
> component_idx, tp->type);
> return 0;
> }
> }
Agreed, and with that:
Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com>
- Arnaldo
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters
2026-06-18 4:46 ` Emil Tsalapatis
2026-06-18 13:27 ` Arnaldo Carvalho de Melo
@ 2026-06-18 14:38 ` Alan Maguire
1 sibling, 0 replies; 11+ messages in thread
From: Alan Maguire @ 2026-06-18 14:38 UTC (permalink / raw)
To: Emil Tsalapatis, Vineet Gupta, dwarves
Cc: bpf, Andrii Nakryiko, acme, jose.marchesi, David Faust,
Yonghong Song
On 18/06/2026 05:46, Emil Tsalapatis wrote:
> On Wed Jun 17, 2026 at 8:57 PM EDT, Vineet Gupta wrote:
>> From: Alan Maguire <alan.maguire@oracle.com>
>>
>> A BTF_KIND_DECL_TAG with a non-negative component_idx applies to a
>> specific function parameter (or struct/union member), not to the
>> function itself. btf_loader.c however attached every decl tag to the
>> type named by btf_type->type, so parameter decl tags were recorded on
>> the function rather than on the parameter, and pfunct never printed
>> them with the parameter.
>>
>> Resolve a non-negative component_idx to the corresponding parameter via
>> new helpers ftype__parameter()/function__parameter(), and attach the
>> tag there. Teach the pretty printer to emit a parameter's attributes by
>> factoring the function-level attribute loop into tag__attributes_fprintf()
>> and calling it from ftype__fprintf_parms() for each parameter.
>>
>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
>
> With one nit:
>
> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
>
> (Also feel free to keep the tags for the other 3 patches since the
> changes are minimal).
>
> </SNIP>
>
>> + if (component_idx >= 0) {
>> + struct tag *func_tag = cu__function(cu, tp->type);
>> +
>> + if (func_tag != NULL) {
>> + tag = function__parameter(tag__function(func_tag), cu,
>> + component_idx);
>> + if (tag == NULL) {
>> + fprintf(stderr, "WARNING: BTF_KIND_DECL_TAG for unknown parameter %d in BTF id %d\n",
>> + component_idx, tp->type);
>> + return 0;
>> + }
>> + }
>> + }
>> +
>> + if (tag == NULL && component_idx < 0)
>> tag = cu__function(cu, tp->type);
>
> Nit, I think this can be simplified as follows:
>
> tag = cu__function(cu, tp->type);
> if (component_idx >= 0 && tag != NULL) {
> tag = function__parameter(tag__function(tag), cu, component_idx);
> if (tag == NULL) {
> fprintf(stderr, "WARNING: BTF_KIND_DECL_TAG for unknown parameter %d in BTF id %d\n",
> component_idx, tp->type);
> return 0;
> }
> }
>
> <SNIP>
yep, that looks good to me. if there are no other issues arising from this
round and Vineet is happy with that change I can roll it in when landing this
to save having to roll a v6.
One other issue; we need to install bpftool for CI. I'm working on a change to
support this (installing the version from the kernel tree), I'll try and get
this in place ASAP. Thanks!
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-06-18 14:39 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-18 0:57 [PAHOLE v5 0/5] support for DW_TAG_GNU_annotation and fixes Vineet Gupta
2026-06-18 0:57 ` [PAHOLE v5 1/5] btf_loader: Handle decl tag component_idx for parameters Vineet Gupta
2026-06-18 4:46 ` Emil Tsalapatis
2026-06-18 13:27 ` Arnaldo Carvalho de Melo
2026-06-18 14:38 ` Alan Maguire
2026-06-18 0:57 ` [PAHOLE v5 2/5] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta
2026-06-18 0:57 ` [PAHOLE v5 3/5] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
2026-06-18 0:57 ` [PAHOLE v5 4/5] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta
2026-06-18 0:57 ` [PAHOLE v5 5/5] tests: Add btf_type_tag ordering test Vineet Gupta
2026-06-18 4:47 ` Emil Tsalapatis
2026-06-18 13:25 ` Arnaldo Carvalho de Melo
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.