* [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC]
@ 2026-06-02 19:55 Vineet Gupta
2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta
` (3 more replies)
0 siblings, 4 replies; 16+ messages in thread
From: Vineet Gupta @ 2026-06-02 19:55 UTC (permalink / raw)
To: dwarves
Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis,
jose.marchesi, David Faust, Vineet Gupta
NFC change preparing for DW_TAG_GNU_annotation support.
Extract the btf_type_tag annotation creation logic into helper
die__add_btf_type_tag().
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
Changes since v3 [3]
- Fix comment to kernel style [Arnaldo]
Changes since v2 [2]
- die__add_btf_type_tag() returns pointer not error code [Alan]
Changes since v1 [1]
- NFC reinstate some original comments
[3] https://lore.kernel.org/bpf/20260601183511.594100-1-vineet.gupta@linux.dev/
[2] https://lore.kernel.org/bpf/20260528223616.2035618-1-vineet.gupta@linux.dev/
[1] https://lore.kernel.org/bpf/20260526181818.4159927-1-vineet.gupta@linux.dev/
---
dwarf_loader.c | 58 +++++++++++++++++++++++++++++---------------------
1 file changed, 34 insertions(+), 24 deletions(-)
diff --git a/dwarf_loader.c b/dwarf_loader.c
index 16fb7becffee..42a0f3f74ce3 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -1600,14 +1600,44 @@ static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *di
return tag;
}
+static struct btf_type_tag_ptr_type *die__add_btf_type_tag(struct btf_type_tag_ptr_type *tag,
+ Dwarf_Die *die, Dwarf_Die *adie,
+ struct cu *cu, struct conf_load *conf)
+{
+ struct btf_type_tag_type *annot;
+ uint32_t id;
+
+ if (tag == NULL) {
+ tag = die__create_new_btf_type_tag_ptr_type(die, cu);
+ if (!tag)
+ return NULL;
+ }
+
+ annot = die__create_new_btf_type_tag_type(adie, cu, conf);
+ if (annot == NULL)
+ return NULL;
+
+ if (cu__table_add_tag(cu, &annot->tag, &id) < 0)
+ return NULL;
+
+ struct dwarf_tag *dtag = tag__dwarf(&annot->tag);
+ dtag->small_id = id;
+ cu__hash(cu, &annot->tag);
+
+ /*
+ * Prepends: for annotations tag1 -> tag2 -> tag3,
+ * the tag->tags list ends up as tag3 -> tag2 -> tag1.
+ */
+ list_add(&annot->node, &tag->tags);
+ return tag;
+}
+
static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu,
struct conf_load *conf)
{
struct btf_type_tag_ptr_type *tag = NULL;
- struct btf_type_tag_type *annot;
Dwarf_Die *cdie, child;
const char *name;
- uint32_t id;
/* If no child tags or skipping btf_type_tag encoding, just create a new tag
* and return
@@ -1627,29 +1657,9 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu,
if (strcmp(name, "btf_type_tag") != 0)
continue;
- if (tag == NULL) {
- /* Create a btf_type_tag_ptr type. */
- tag = die__create_new_btf_type_tag_ptr_type(die, cu);
- if (!tag)
- return NULL;
- }
-
- /* Create a btf_type_tag type for this annotation. */
- annot = die__create_new_btf_type_tag_type(cdie, cu, conf);
- if (annot == NULL)
- return NULL;
-
- if (cu__table_add_tag(cu, &annot->tag, &id) < 0)
+ tag = die__add_btf_type_tag(tag, die, cdie, cu, conf);
+ if (tag == NULL)
return NULL;
-
- struct dwarf_tag *dtag = tag__dwarf(&annot->tag);
- dtag->small_id = id;
- cu__hash(cu, &annot->tag);
-
- /* For a list of DW_TAG_LLVM_annotation like tag1 -> tag2 -> tag3,
- * the tag->tags contains tag3 -> tag2 -> tag1.
- */
- list_add(&annot->node, &tag->tags);
} while (dwarf_siblingof(cdie, cdie) == 0);
return tag ? &tag->tag : tag__new(die, cu);
--
2.54.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation 2026-06-02 19:55 [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta @ 2026-06-02 19:55 ` Vineet Gupta 2026-06-03 20:08 ` Yonghong Song ` (3 more replies) 2026-06-02 19:55 ` [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta ` (2 subsequent siblings) 3 siblings, 4 replies; 16+ messages in thread From: Vineet Gupta @ 2026-06-02 19:55 UTC (permalink / raw) To: dwarves Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis, jose.marchesi, David Faust, Vineet Gupta gcc 16 was the first release to support DW_TAG_GNU_annotations and this patch enables the same in pahole. Bulk of changes are dwarf_loader but btf_encoder also gains support with minimal changes. GCC encodes btf_type_tag and btf_decl_tag annotations differently from LLVM. While LLVM uses DW_TAG_LLVM_annotation (0x6000) as child DIEs, GCC uses DW_TAG_GNU_annotation (0x6001) as standalone sibling DIEs referenced via DW_AT_GNU_annotation (0x2139) attributes, with chaining through the same attribute on annotation DIEs themselves. Handle both encoding styles: For btf_type_tag (pointer annotations): - Recognize DW_TAG_GNU_annotation alongside DW_TAG_LLVM_annotation in child annotation scanning. - Follow DW_AT_GNU_annotation attribute chains on pointer types for GCC-style btf_type_tag resolution. - Normalize DW_TAG_GNU_annotation to DW_TAG_LLVM_annotation in the internal representation so downstream code works unchanged. For btf_decl_tag (function/struct/member annotations): - Add add_gnu_annotation_chain() to follow DW_AT_GNU_annotation attribute chains on function, struct, and member DIEs. - GCC puts DW_AT_GNU_annotation on the function/struct DIE itself (not as child DIEs), referencing sibling annotation DIEs that chain via the same attribute. Also: - Silently skip standalone DW_TAG_GNU_annotation DIEs at CU level. - Add tag__is_annotation() helper macro for annotation tag checks. - Rename add_llvm_annotation -> add_tag_annotation, skip_llvm_annotations -> skip_tag_annotations since these now handle both LLVM and GNU annotation formats. Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev> --- Changes since v3 [3] - Add helper tag__is_annotation [Emil] - Rename some functions containing "llvm" as they are common to LLVM/GCC tags [Emil] - Deduplicate checks before loop and inside loop in add_gnu_annotation_chain() and check_gnu_attr [Arnaldo] - Fix some typos and move some comments [Emi] Changes since v2 [2] - Removed loop detection logic [Alan] - Move test changes to different patch [Alan] Changes since v1 [1] - NFC Reduce indentation with early exits (Alexei offlist) [3] https://lore.kernel.org/bpf/20260601183511.594100-2-vineet.gupta@linux.dev/ [2] https://lore.kernel.org/bpf/20260528223616.2035618-2-vineet.gupta@linux.dev/ [1] https://lore.kernel.org/bpf/20260526181818.4159927-2-vineet.gupta@linux.dev/ --- btf_encoder.c | 1 + dutil.h | 11 +++++ dwarf_loader.c | 105 +++++++++++++++++++++++++++++++++++++++------- dwarves.h | 2 +- dwarves_fprintf.c | 12 ++++-- 5 files changed, 110 insertions(+), 21 deletions(-) diff --git a/btf_encoder.c b/btf_encoder.c index 633bc6162ce0..d5af706d7638 100644 --- a/btf_encoder.c +++ b/btf_encoder.c @@ -1831,6 +1831,7 @@ static int btf_encoder__encode_tag(struct btf_encoder *encoder, struct tag *tag, name = namespace__name(tag__namespace(tag)); return btf_encoder__add_ref_type(encoder, BTF_KIND_TYPEDEF, ref_type_id, name, false); case DW_TAG_LLVM_annotation: + case DW_TAG_GNU_annotation: name = tag__btf_type_tag(tag)->value; return btf_encoder__add_ref_type(encoder, BTF_KIND_TYPE_TAG, ref_type_id, name, false); case DW_TAG_structure_type: diff --git a/dutil.h b/dutil.h index ff78aa6dfd10..be02c68cf4a7 100644 --- a/dutil.h +++ b/dutil.h @@ -35,6 +35,17 @@ #define DW_TAG_LLVM_annotation 0x6000 #endif +#ifndef DW_TAG_GNU_annotation +#define DW_TAG_GNU_annotation 0x6001 +#endif + +#ifndef DW_AT_GNU_annotation +#define DW_AT_GNU_annotation 0x2139 +#endif + +#define tag__is_annotation(tag) \ + ((tag) == DW_TAG_LLVM_annotation || (tag) == DW_TAG_GNU_annotation) + static inline __attribute__((const)) bool is_power_of_2(unsigned long n) { return (n != 0 && ((n & (n - 1)) == 0)); diff --git a/dwarf_loader.c b/dwarf_loader.c index 42a0f3f74ce3..14f71c9043aa 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -908,7 +908,12 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b return -ENOMEM; } -static int add_llvm_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf, +static bool die__tag_is_annotation(Dwarf_Die *die) +{ + return tag__is_annotation(dwarf_tag(die)); +} + +static int add_tag_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf, struct list_head *head) { struct llvm_annotation *annot; @@ -943,16 +948,40 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx, die = &child; do { - if (dwarf_tag(die) == DW_TAG_LLVM_annotation) { - ret = add_llvm_annotation(die, component_idx, conf, head); - if (ret) - return ret; - } + if (!die__tag_is_annotation(die)) + continue; + + ret = add_tag_annotation(die, component_idx, conf, head); + if (ret) + return ret; } while (dwarf_siblingof(die, die) == 0); return 0; } +/* + * Handle gcc style btf_decl_tag annotations for functions/struct/member tags. + * Pointers are handled separately, inline in die__create_new_pointer_tag() + */ +static int add_gnu_annotation_chain(Dwarf_Die *die, int component_idx, + struct conf_load *conf, struct list_head *head) +{ + Dwarf_Attribute attr; + Dwarf_Die annot_die; + + while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL && + dwarf_formref_die(&attr, &annot_die) != NULL && + dwarf_tag(&annot_die) == DW_TAG_GNU_annotation) { + int ret = add_tag_annotation(&annot_die, component_idx, conf, head); + if (ret) + return ret; + + die = &annot_die; + } + + return 0; +} + int class_member__dwarf_recode_bitfield(struct class_member *member, struct cu *cu) { @@ -1596,6 +1625,8 @@ static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *di return NULL; tag__init(&tag->tag, cu, die); + /* Normalize DW_TAG_GNU_annotation to DW_TAG_LLVM_annotation internally */ + tag->tag.tag = DW_TAG_LLVM_annotation; tag->value = attr_string(die, DW_AT_const_value, conf); return tag; } @@ -1637,19 +1668,21 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu, { struct btf_type_tag_ptr_type *tag = NULL; Dwarf_Die *cdie, child; + Dwarf_Attribute attr; + Dwarf_Die annot_die; const char *name; - /* If no child tags or skipping btf_type_tag encoding, just create a new tag - * and return - */ - if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0 || - conf->skip_encoding_btf_type_tag) + /* If skipping btf_type_tag encoding, just create a new tag, return */ + if (conf->skip_encoding_btf_type_tag) return tag__new(die, cu); - /* Otherwise, check DW_TAG_LLVM_annotation child tags */ + if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0) + goto check_gnu_attr; + + /* Handle LLVM style annotation tags if present */ cdie = &child; do { - if (dwarf_tag(cdie) != DW_TAG_LLVM_annotation) + if (!die__tag_is_annotation(cdie)) continue; /* Only check btf_type_tag annotations */ @@ -1662,6 +1695,26 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu, return NULL; } while (dwarf_siblingof(cdie, cdie) == 0); +check_gnu_attr: + if (tag != NULL) + goto out; + + /* Handle GCC-style DW_AT_GNU_annotation attribute */ + while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL && + dwarf_formref_die(&attr, &annot_die) != NULL && + dwarf_tag(&annot_die) == DW_TAG_GNU_annotation) { + name = attr_string(&annot_die, DW_AT_name, conf); + if (strcmp(name, "btf_type_tag") != 0) + break; + + tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf); + if (tag == NULL) + return NULL; + + die = &annot_die; + } + +out: return tag ? &tag->tag : tag__new(die, cu); } @@ -1690,6 +1743,12 @@ static struct tag *die__create_new_class(Dwarf_Die *die, struct cu *cu, struct c } } + if (class != NULL && + add_gnu_annotation_chain(die, -1, conf, &class->type.namespace.annots) != 0) { + class__delete(class, cu); + class = NULL; + } + return class ? &class->type.namespace.tag : NULL; } @@ -2051,11 +2110,14 @@ static int die__process_class(Dwarf_Die *die, struct type *class, cu__hash(cu, &member->tag); if (add_child_llvm_annotations(die, member_idx, conf, &class->namespace.annots)) return -ENOMEM; + if (add_gnu_annotation_chain(die, member_idx, conf, &class->namespace.annots)) + return -ENOMEM; member_idx++; } continue; case DW_TAG_LLVM_annotation: - if (add_llvm_annotation(die, -1, conf, &class->namespace.annots)) + case DW_TAG_GNU_annotation: + if (add_tag_annotation(die, -1, conf, &class->namespace.annots)) return -ENOMEM; continue; default: { @@ -2360,7 +2422,8 @@ static int die__process_function(Dwarf_Die *die, struct ftype *ftype, goto out_enomem; continue; case DW_TAG_LLVM_annotation: - if (add_llvm_annotation(die, -1, conf, &(tag__function(&ftype->tag)->annots))) + case DW_TAG_GNU_annotation: + if (add_tag_annotation(die, -1, conf, &(tag__function(&ftype->tag)->annots))) goto out_enomem; continue; default: @@ -2408,6 +2471,12 @@ static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *cu, struc function = NULL; } + if (function != NULL && + add_gnu_annotation_chain(die, -1, conf, &function->annots) != 0) { + function__delete(function, cu); + function = NULL; + } + return function ? &function->proto.tag : NULL; } @@ -2469,6 +2538,9 @@ static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu, */ tag = &unsupported_tag; break; + case DW_TAG_GNU_annotation: + tag = &unsupported_tag; + break; case DW_TAG_label: if (conf->ignore_labels) tag = &unsupported_tag; // callers will assume conf->ignore_labels is true @@ -2494,7 +2566,8 @@ static int die__process_unit(Dwarf_Die *die, struct cu *cu, struct conf_load *co // XXX special case DW_TAG_dwarf_procedure, appears when looking at a recent ~/bin/perf // Investigate later how to properly support this... if (dwarf_tag(die) != DW_TAG_dwarf_procedure && - dwarf_tag(die) != DW_TAG_label) // conf->ignore_labels == true, see die__process_tag() + dwarf_tag(die) != DW_TAG_label && // conf->ignore_labels == true, see die__process_tag() + dwarf_tag(die) != DW_TAG_GNU_annotation) tag__print_not_supported(die); continue; } diff --git a/dwarves.h b/dwarves.h index 5ec16e750e83..75c311a2f8ee 100644 --- a/dwarves.h +++ b/dwarves.h @@ -670,7 +670,7 @@ static inline int tag__is_tag_type(const struct tag *tag) tag->tag == DW_TAG_volatile_type || tag->tag == DW_TAG_atomic_type || tag->tag == DW_TAG_unspecified_type || - tag->tag == DW_TAG_LLVM_annotation; + tag__is_annotation(tag->tag); } static inline const char *tag__decl_file(const struct tag *tag, diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c index 1ec478c2a027..757e4992a1f7 100644 --- a/dwarves_fprintf.c +++ b/dwarves_fprintf.c @@ -140,6 +140,8 @@ const char *dwarf_tag_name(const uint32_t tag) return dwarf_gnu_tag_names[tag - DW_TAG_MIPS_loop]; else if (tag == DW_TAG_LLVM_annotation) return "LLVM_annotation"; + else if (tag == DW_TAG_GNU_annotation) + return "GNU_annotation"; return "INVALID"; } @@ -658,6 +660,7 @@ static const char *__tag__name(const struct tag *tag, const struct cu *cu, snprintf(bf, len, "%s", variable__name(tag__variable(tag))); break; case DW_TAG_LLVM_annotation: + case DW_TAG_GNU_annotation: type = cu__type(cu, tag->type); if (type == NULL && tag->type != 0) tag__id_not_found_snprintf(bf, len, tag->type); @@ -723,7 +726,7 @@ static size_t type__fprintf_stats(struct type *type, const struct cu *cu, return printed; } -static type_id_t skip_llvm_annotations(const struct cu *cu, type_id_t id) +static type_id_t skip_tag_annotations(const struct cu *cu, type_id_t id) { struct tag *type; @@ -731,7 +734,7 @@ static type_id_t skip_llvm_annotations(const struct cu *cu, type_id_t id) if (id == 0) break; type = cu__type(cu, id); - if (type == NULL || type->tag != DW_TAG_LLVM_annotation || type->type == id) + if (type == NULL || !tag__is_annotation(type->tag) || type->type == id) break; id = type->type; } @@ -838,7 +841,7 @@ inner_struct: next_type: switch (type->tag) { case DW_TAG_pointer_type: { - type_id_t ptype_id = skip_llvm_annotations(cu, type->type); + type_id_t ptype_id = skip_tag_annotations(cu, type->type); if (ptype_id != 0) { int n; @@ -936,7 +939,8 @@ print_modifier: { else printed += enumeration__fprintf(type, &tconf, fp); break; - case DW_TAG_LLVM_annotation: { + case DW_TAG_LLVM_annotation: + case DW_TAG_GNU_annotation: { struct tag *ttype = cu__type(cu, type->type); if (ttype) { type = ttype; -- 2.54.0 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation 2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta @ 2026-06-03 20:08 ` Yonghong Song 2026-06-03 20:54 ` Vineet Gupta 2026-06-17 18:18 ` Vineet Gupta 2026-06-03 20:42 ` Emil Tsalapatis ` (2 subsequent siblings) 3 siblings, 2 replies; 16+ messages in thread From: Yonghong Song @ 2026-06-03 20:08 UTC (permalink / raw) To: Vineet Gupta, dwarves Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis, jose.marchesi, David Faust On 6/2/26 12:55 PM, Vineet Gupta wrote: > gcc 16 was the first release to support DW_TAG_GNU_annotations and this > patch enables the same in pahole. Bulk of changes are dwarf_loader but > btf_encoder also gains support with minimal changes. > > GCC encodes btf_type_tag and btf_decl_tag annotations differently from > LLVM. While LLVM uses DW_TAG_LLVM_annotation (0x6000) as child DIEs, > GCC uses DW_TAG_GNU_annotation (0x6001) as standalone sibling DIEs > referenced via DW_AT_GNU_annotation (0x2139) attributes, with chaining > through the same attribute on annotation DIEs themselves. > > Handle both encoding styles: > > For btf_type_tag (pointer annotations): > - Recognize DW_TAG_GNU_annotation alongside DW_TAG_LLVM_annotation in > child annotation scanning. > - Follow DW_AT_GNU_annotation attribute chains on pointer types for > GCC-style btf_type_tag resolution. > - Normalize DW_TAG_GNU_annotation to DW_TAG_LLVM_annotation in the > internal representation so downstream code works unchanged. > > For btf_decl_tag (function/struct/member annotations): > - Add add_gnu_annotation_chain() to follow DW_AT_GNU_annotation > attribute chains on function, struct, and member DIEs. > - GCC puts DW_AT_GNU_annotation on the function/struct DIE itself > (not as child DIEs), referencing sibling annotation DIEs that chain > via the same attribute. > > Also: > - Silently skip standalone DW_TAG_GNU_annotation DIEs at CU level. > - Add tag__is_annotation() helper macro for annotation tag checks. > - Rename add_llvm_annotation -> add_tag_annotation, > skip_llvm_annotations -> skip_tag_annotations since these now > handle both LLVM and GNU annotation formats. > > Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev> > --- > Changes since v3 [3] > - Add helper tag__is_annotation [Emil] > - Rename some functions containing "llvm" as they are common to LLVM/GCC tags [Emil] > - Deduplicate checks before loop and inside loop in add_gnu_annotation_chain() and check_gnu_attr [Arnaldo] > - Fix some typos and move some comments [Emi] > > Changes since v2 [2] > - Removed loop detection logic [Alan] > - Move test changes to different patch [Alan] > > Changes since v1 [1] > - NFC Reduce indentation with early exits (Alexei offlist) > > [3] https://lore.kernel.org/bpf/20260601183511.594100-2-vineet.gupta@linux.dev/ > [2] https://lore.kernel.org/bpf/20260528223616.2035618-2-vineet.gupta@linux.dev/ > [1] https://lore.kernel.org/bpf/20260526181818.4159927-2-vineet.gupta@linux.dev/ I download and build gcc-16.1 which will be used for gcc compilation. I did some experiments with this patch set for both type tag and decl tag. See below. For type tag ============ $ cat type_tag.c /* btf_type_tag test cases. * * btf_type_tag annotates a *type* and the tag becomes part of the type * chain in BTF (TYPE_TAG kind), e.g. used by the kernel for __user / __rcu * / __percpu style annotations. * * Placement matters: the tag is written *before* the '*' so it annotates * the pointee type. This produces PTR -> TYPE_TAG 'x' -> <pointee>. * * Build: clang -O2 -g -target bpf -c type_tag.c -o type_tag.o * /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c type_tag.c -o type_tag.o * Dump: bpftool btf dump file type_tag.o */ #define __type_tag(x) __attribute__((btf_type_tag(x))) /* a single type tag on a pointer's pointee */ int __type_tag("user") *user_ptr_var; /* stacked type tags: the chain is preserved, outermost first */ int __type_tag("tag1") __type_tag("tag2") *stacked_var; /* type tags inside struct members */ struct bar { char __type_tag("rcu") *name; int __type_tag("percpu") *counter; }; /* type tag on a function parameter's pointee */ int read_user(int __type_tag("user") *p) { return *p; } /* keep things referenced */ int use(struct bar *b) { return read_user(user_ptr_var) + (stacked_var ? *stacked_var : 0) + (b->counter ? *b->counter : 0); } $ /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c type_tag.c -o type_tag.o $ bpftool btf dump file type_tag.o [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED [2] PTR '(anon)' type_id=15 [3] PTR '(anon)' type_id=17 [4] STRUCT 'bar' size=16 vlen=2 'name' type_id=6 bits_offset=0 'counter' type_id=7 bits_offset=64 [5] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED [6] PTR '(anon)' type_id=18 [7] PTR '(anon)' type_id=19 [8] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1 'b' type_id=9 [9] PTR '(anon)' type_id=4 [10] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1 'p' type_id=2 [11] VAR 'stacked_var' type_id=3, linkage=global [12] VAR 'user_ptr_var' type_id=2, linkage=global [13] FUNC 'use' type_id=8 linkage=global [14] FUNC 'read_user' type_id=10 linkage=global [15] TYPE_TAG 'user' type_id=1 [16] TYPE_TAG 'tag1' type_id=1 [17] TYPE_TAG 'tag2' type_id=16 [18] TYPE_TAG 'rcu' type_id=5 [19] TYPE_TAG 'percpu' type_id=1 [20] DATASEC '.bss' size=0 vlen=2 type_id=11 offset=0 size=8 (VAR 'stacked_var') type_id=12 offset=0 size=8 (VAR 'user_ptr_var') $ $ clang -O2 -g -target bpf -c type_tag.c -o type_tag.o $ bpftool btf dump file type_tag.o [1] TYPE_TAG 'user' type_id=3 [2] PTR '(anon)' type_id=1 [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED [4] FUNC_PROTO '(anon)' ret_type_id=3 vlen=1 'p' type_id=2 [5] FUNC 'read_user' type_id=4 linkage=global [6] PTR '(anon)' type_id=7 [7] STRUCT 'bar' size=16 vlen=2 'name' type_id=9 bits_offset=0 'counter' type_id=12 bits_offset=64 [8] TYPE_TAG 'rcu' type_id=10 [9] PTR '(anon)' type_id=8 [10] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED [11] TYPE_TAG 'percpu' type_id=3 [12] PTR '(anon)' type_id=11 [13] FUNC_PROTO '(anon)' ret_type_id=3 vlen=1 'b' type_id=6 [14] FUNC 'use' type_id=13 linkage=global [15] VAR 'user_ptr_var' type_id=2, linkage=global [16] TYPE_TAG 'tag1' type_id=3 [17] TYPE_TAG 'tag2' type_id=16 [18] PTR '(anon)' type_id=17 [19] VAR 'stacked_var' type_id=18, linkage=global [20] DATASEC '.bss' size=0 vlen=2 type_id=15 offset=0 size=8 (VAR 'user_ptr_var') type_id=19 offset=0 size=8 (VAR 'stacked_var') $ So type tag matches between clang and gcc. For decl tag ============ $ cat decl_tag.c /* btf_decl_tag test cases. * * btf_decl_tag can be attached to: * - global (incl. static) variables * - functions * - function parameters * - struct/union types and their members * - typedefs * * Build: clang -O2 -target bpf -g -c decl_tag.c -o decl_tag.o * /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c decl_tag.c -o decl_tag.o * Dump: bpftool btf dump file decl_tag.o */ #define __tag(x) __attribute__((btf_decl_tag(x))) /* tag on a global variable */ int global_var __tag("global_var_tag"); /* tag on a static variable */ static int static_var __tag("static_var_tag"); /* multiple tags on one declaration */ int multi_tag_var __tag("tag_a") __tag("tag_b"); /* tag on struct type and its members */ struct foo { int a __tag("member_a_tag"); int b __tag("member_b_tag"); } __tag("struct_foo_tag"); /* tag on a typedef */ typedef struct foo foo_t1 __tag("typedef_foo_tag"); typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag"); /* tag on a function and its parameters */ __tag("func_add_tag") int add(int x __tag("param_x_tag"), int y __tag("param_y_tag")) { return x + y; } /* keep the globals/types alive so they land in BTF */ int use(foo_t1 *f, foo_t2 *g) { return add(global_var + static_var + multi_tag_var, f->a + g->foo2); } $ /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c decl_tag.c -o decl_tag.o decl_tag.c:30:1: warning: ‘btf_decl_tag’ attribute does not apply to types [-Wattributes] 30 | } __tag("struct_foo_tag"); | ^ $ bpftool btf dump file decl_tag.o [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED [2] STRUCT 'foo' size=8 vlen=2 'a' type_id=1 bits_offset=0 'b' type_id=1 bits_offset=32 [3] TYPEDEF 'foo_t1' type_id=2 [4] STRUCT '(anon)' size=4 vlen=1 'foo2' type_id=1 bits_offset=0 [5] TYPEDEF 'foo_t2' type_id=4 [6] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2 'f' type_id=7 'g' type_id=8 [7] PTR '(anon)' type_id=3 [8] PTR '(anon)' type_id=5 [9] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2 'x' type_id=1 'y' type_id=1 [10] VAR 'multi_tag_var' type_id=1, linkage=global [11] VAR 'global_var' type_id=1, linkage=global [12] FUNC 'use' type_id=6 linkage=global [13] FUNC 'add' type_id=9 linkage=global [14] DECL_TAG 'global_var_tag' type_id=11 component_idx=-1 [15] DECL_TAG 'static_var_tag' type_id=0 component_idx=-1 [16] DECL_TAG 'tag_b' type_id=10 component_idx=-1 [17] DECL_TAG 'tag_a' type_id=10 component_idx=-1 [18] DECL_TAG 'member_a_tag' type_id=2 component_idx=0 [19] DECL_TAG 'member_b_tag' type_id=2 component_idx=1 [20] DECL_TAG 'param_x_tag' type_id=13 component_idx=0 [21] DECL_TAG 'param_y_tag' type_id=13 component_idx=1 [22] DECL_TAG 'func_add_tag' type_id=13 component_idx=-1 [23] DATASEC '.bss' size=0 vlen=2 type_id=10 offset=0 size=4 (VAR 'multi_tag_var') type_id=11 offset=0 size=4 (VAR 'global_var') $ bpftool btf dump file decl_tag.o | grep DECL_TAG [14] DECL_TAG 'global_var_tag' type_id=11 component_idx=-1 [15] DECL_TAG 'static_var_tag' type_id=0 component_idx=-1 [16] DECL_TAG 'tag_b' type_id=10 component_idx=-1 [17] DECL_TAG 'tag_a' type_id=10 component_idx=-1 [18] DECL_TAG 'member_a_tag' type_id=2 component_idx=0 [19] DECL_TAG 'member_b_tag' type_id=2 component_idx=1 [20] DECL_TAG 'param_x_tag' type_id=13 component_idx=0 [21] DECL_TAG 'param_y_tag' type_id=13 component_idx=1 [22] DECL_TAG 'func_add_tag' type_id=13 component_idx=-1 $ Three decl tags (struct_foo_tag, typedef_foo_tag and typedef_foo2_tag) are missing here: struct foo { int a __tag("member_a_tag"); int b __tag("member_b_tag"); } __tag("struct_foo_tag"); /* tag on a typedef */ typedef struct foo foo_t1 __tag("typedef_foo_tag"); typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag"); $ clang -O2 -g -target bpf -c decl_tag.c -o decl_tag.o $ bpftool btf dump file decl_tag.o [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED [2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2 'x' type_id=1 'y' type_id=1 [3] FUNC 'add' type_id=2 linkage=global [4] DECL_TAG 'param_x_tag' type_id=3 component_idx=0 [5] DECL_TAG 'param_y_tag' type_id=3 component_idx=1 [6] DECL_TAG 'func_add_tag' type_id=3 component_idx=-1 [7] PTR '(anon)' type_id=8 [8] TYPEDEF 'foo_t1' type_id=10 [9] DECL_TAG 'typedef_foo_tag' type_id=8 component_idx=-1 [10] STRUCT 'foo' size=8 vlen=2 'a' type_id=1 bits_offset=0 'b' type_id=1 bits_offset=32 [11] DECL_TAG 'struct_foo_tag' type_id=10 component_idx=-1 [12] DECL_TAG 'member_a_tag' type_id=10 component_idx=0 [13] DECL_TAG 'member_b_tag' type_id=10 component_idx=1 [14] PTR '(anon)' type_id=15 [15] TYPEDEF 'foo_t2' type_id=17 [16] DECL_TAG 'typedef_foo2_tag' type_id=15 component_idx=-1 [17] STRUCT '(anon)' size=4 vlen=1 'foo2' type_id=1 bits_offset=0 [18] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2 'f' type_id=7 'g' type_id=14 [19] FUNC 'use' type_id=18 linkage=global [20] VAR 'global_var' type_id=1, linkage=global [21] DECL_TAG 'global_var_tag' type_id=20 component_idx=-1 [22] VAR 'multi_tag_var' type_id=1, linkage=global [23] DECL_TAG 'tag_a' type_id=22 component_idx=-1 [24] DECL_TAG 'tag_b' type_id=22 component_idx=-1 [25] DATASEC '.bss' size=0 vlen=2 type_id=20 offset=0 size=4 (VAR 'global_var') type_id=22 offset=0 size=4 (VAR 'multi_tag_var') $ bpftool btf dump file decl_tag.o | grep DECL_TAG [4] DECL_TAG 'param_x_tag' type_id=3 component_idx=0 [5] DECL_TAG 'param_y_tag' type_id=3 component_idx=1 [6] DECL_TAG 'func_add_tag' type_id=3 component_idx=-1 [9] DECL_TAG 'typedef_foo_tag' type_id=8 component_idx=-1 [11] DECL_TAG 'struct_foo_tag' type_id=10 component_idx=-1 [12] DECL_TAG 'member_a_tag' type_id=10 component_idx=0 [13] DECL_TAG 'member_b_tag' type_id=10 component_idx=1 [16] DECL_TAG 'typedef_foo2_tag' type_id=15 component_idx=-1 [21] DECL_TAG 'global_var_tag' type_id=20 component_idx=-1 [23] DECL_TAG 'tag_a' type_id=22 component_idx=-1 [24] DECL_TAG 'tag_b' type_id=22 component_idx=-1 DECL_TAG 'static_var_tag' type_id=0 component_idx=-1 is missing in llvm. This is execpted for llvm since 'static_var' is 'inlined' since it is value is 0 and the compiler optimization removed it in function use(). In llvm Dwarf->BTF conversion is very late and we only emit survived globals. gcc emits 'static_var_tag' probably in frontend. Emitting 'static_var_tag' is okay, just not used. I think gcc should support - declaration tag for typedef, and - declaration tag for the whole struct (like above 'struct_foo_tag') to be compatible with llvm. > --- > btf_encoder.c | 1 + > dutil.h | 11 +++++ > dwarf_loader.c | 105 +++++++++++++++++++++++++++++++++++++++------- > dwarves.h | 2 +- > dwarves_fprintf.c | 12 ++++-- > 5 files changed, 110 insertions(+), 21 deletions(-) [...] ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation 2026-06-03 20:08 ` Yonghong Song @ 2026-06-03 20:54 ` Vineet Gupta 2026-06-03 21:40 ` Yonghong Song 2026-06-17 18:18 ` Vineet Gupta 1 sibling, 1 reply; 16+ messages in thread From: Vineet Gupta @ 2026-06-03 20:54 UTC (permalink / raw) To: Yonghong Song, dwarves Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis, jose.marchesi, David Faust Hi Yonghong, Thanks for reviewing the patches. On 6/3/26 1:08 PM, Yonghong Song wrote: >> [3] https://lore.kernel.org/bpf/20260601183511.594100-2-vineet.gupta@linux.dev/ >> [2] https://lore.kernel.org/bpf/20260528223616.2035618-2-vineet.gupta@linux.dev/ >> [1] https://lore.kernel.org/bpf/20260526181818.4159927-2-vineet.gupta@linux.dev/ > I download and build gcc-16.1 which will be used for gcc compilation. > I did some experiments with this patch set for both type tag and decl tag. > See below. FWIW, you definitely need a fix [1] on top of gcc 16.1. The patch explains the reasoning and did cause some missing entries in type chain. I haven't checked if this changes anything specific to issues you point below. [1] https://gcc.gnu.org/pipermail/bpf/2026-May/000147.html > > For type tag > ============ > > $ cat type_tag.c > /* btf_type_tag test cases. > * > * btf_type_tag annotates a *type* and the tag becomes part of the type > * chain in BTF (TYPE_TAG kind), e.g. used by the kernel for __user / __rcu > * / __percpu style annotations. > * > * Placement matters: the tag is written *before* the '*' so it annotates > * the pointee type. This produces PTR -> TYPE_TAG 'x' -> <pointee>. > * > * Build: clang -O2 -g -target bpf -c type_tag.c -o type_tag.o > * /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c type_tag.c -o type_tag.o > * Dump: bpftool btf dump file type_tag.o > */ > > #define __type_tag(x) __attribute__((btf_type_tag(x))) > > /* a single type tag on a pointer's pointee */ > int __type_tag("user") *user_ptr_var; > > /* stacked type tags: the chain is preserved, outermost first */ > int __type_tag("tag1") __type_tag("tag2") *stacked_var; > > /* type tags inside struct members */ > struct bar { > char __type_tag("rcu") *name; > int __type_tag("percpu") *counter; > }; > > /* type tag on a function parameter's pointee */ > int read_user(int __type_tag("user") *p) > { > return *p; > } > > /* keep things referenced */ > int use(struct bar *b) > { > return read_user(user_ptr_var) + (stacked_var ? *stacked_var : 0) + > (b->counter ? *b->counter : 0); > } > > $ /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c type_tag.c -o type_tag.o > $ bpftool btf dump file type_tag.o > [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED > [2] PTR '(anon)' type_id=15 > [3] PTR '(anon)' type_id=17 > [4] STRUCT 'bar' size=16 vlen=2 > 'name' type_id=6 bits_offset=0 > 'counter' type_id=7 bits_offset=64 > [5] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED > [6] PTR '(anon)' type_id=18 > [7] PTR '(anon)' type_id=19 > [8] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1 > 'b' type_id=9 > [9] PTR '(anon)' type_id=4 > [10] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1 > 'p' type_id=2 > [11] VAR 'stacked_var' type_id=3, linkage=global > [12] VAR 'user_ptr_var' type_id=2, linkage=global > [13] FUNC 'use' type_id=8 linkage=global > [14] FUNC 'read_user' type_id=10 linkage=global > [15] TYPE_TAG 'user' type_id=1 > [16] TYPE_TAG 'tag1' type_id=1 > [17] TYPE_TAG 'tag2' type_id=16 > [18] TYPE_TAG 'rcu' type_id=5 > [19] TYPE_TAG 'percpu' type_id=1 > [20] DATASEC '.bss' size=0 vlen=2 > type_id=11 offset=0 size=8 (VAR 'stacked_var') > type_id=12 offset=0 size=8 (VAR 'user_ptr_var') > $ > > $ clang -O2 -g -target bpf -c type_tag.c -o type_tag.o > $ bpftool btf dump file type_tag.o > [1] TYPE_TAG 'user' type_id=3 > [2] PTR '(anon)' type_id=1 > [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED > [4] FUNC_PROTO '(anon)' ret_type_id=3 vlen=1 > 'p' type_id=2 > [5] FUNC 'read_user' type_id=4 linkage=global > [6] PTR '(anon)' type_id=7 > [7] STRUCT 'bar' size=16 vlen=2 > 'name' type_id=9 bits_offset=0 > 'counter' type_id=12 bits_offset=64 > [8] TYPE_TAG 'rcu' type_id=10 > [9] PTR '(anon)' type_id=8 > [10] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED > [11] TYPE_TAG 'percpu' type_id=3 > [12] PTR '(anon)' type_id=11 > [13] FUNC_PROTO '(anon)' ret_type_id=3 vlen=1 > 'b' type_id=6 > [14] FUNC 'use' type_id=13 linkage=global > [15] VAR 'user_ptr_var' type_id=2, linkage=global > [16] TYPE_TAG 'tag1' type_id=3 > [17] TYPE_TAG 'tag2' type_id=16 > [18] PTR '(anon)' type_id=17 > [19] VAR 'stacked_var' type_id=18, linkage=global > [20] DATASEC '.bss' size=0 vlen=2 > type_id=15 offset=0 size=8 (VAR 'user_ptr_var') > type_id=19 offset=0 size=8 (VAR 'stacked_var') > $ > > So type tag matches between clang and gcc. Nice, thanks for cross-checking and confirming. > For decl tag > ============ > > $ cat decl_tag.c > /* btf_decl_tag test cases. > * > * btf_decl_tag can be attached to: > * - global (incl. static) variables > * - functions > * - function parameters > * - struct/union types and their members > * - typedefs > * > * Build: clang -O2 -target bpf -g -c decl_tag.c -o decl_tag.o > * /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c decl_tag.c -o decl_tag.o > * Dump: bpftool btf dump file decl_tag.o > */ > > #define __tag(x) __attribute__((btf_decl_tag(x))) > > /* tag on a global variable */ > int global_var __tag("global_var_tag"); > > /* tag on a static variable */ > static int static_var __tag("static_var_tag"); > > /* multiple tags on one declaration */ > int multi_tag_var __tag("tag_a") __tag("tag_b"); > > /* tag on struct type and its members */ > struct foo { > int a __tag("member_a_tag"); > int b __tag("member_b_tag"); > } __tag("struct_foo_tag"); > > /* tag on a typedef */ > typedef struct foo foo_t1 __tag("typedef_foo_tag"); > typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag"); > > /* tag on a function and its parameters */ > __tag("func_add_tag") > int add(int x __tag("param_x_tag"), int y __tag("param_y_tag")) > { > return x + y; > } > > /* keep the globals/types alive so they land in BTF */ > int use(foo_t1 *f, foo_t2 *g) > { > return add(global_var + static_var + multi_tag_var, f->a + g->foo2); > } > > $ /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c decl_tag.c -o decl_tag.o > decl_tag.c:30:1: warning: ‘btf_decl_tag’ attribute does not apply to types [-Wattributes] > 30 | } __tag("struct_foo_tag"); > | ^ > > $ bpftool btf dump file decl_tag.o > [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED > [2] STRUCT 'foo' size=8 vlen=2 > 'a' type_id=1 bits_offset=0 > 'b' type_id=1 bits_offset=32 > [3] TYPEDEF 'foo_t1' type_id=2 > [4] STRUCT '(anon)' size=4 vlen=1 > 'foo2' type_id=1 bits_offset=0 > [5] TYPEDEF 'foo_t2' type_id=4 > [6] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2 > 'f' type_id=7 > 'g' type_id=8 > [7] PTR '(anon)' type_id=3 > [8] PTR '(anon)' type_id=5 > [9] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2 > 'x' type_id=1 > 'y' type_id=1 > [10] VAR 'multi_tag_var' type_id=1, linkage=global > [11] VAR 'global_var' type_id=1, linkage=global > [12] FUNC 'use' type_id=6 linkage=global > [13] FUNC 'add' type_id=9 linkage=global > [14] DECL_TAG 'global_var_tag' type_id=11 component_idx=-1 > [15] DECL_TAG 'static_var_tag' type_id=0 component_idx=-1 > [16] DECL_TAG 'tag_b' type_id=10 component_idx=-1 > [17] DECL_TAG 'tag_a' type_id=10 component_idx=-1 > [18] DECL_TAG 'member_a_tag' type_id=2 component_idx=0 > [19] DECL_TAG 'member_b_tag' type_id=2 component_idx=1 > [20] DECL_TAG 'param_x_tag' type_id=13 component_idx=0 > [21] DECL_TAG 'param_y_tag' type_id=13 component_idx=1 > [22] DECL_TAG 'func_add_tag' type_id=13 component_idx=-1 > [23] DATASEC '.bss' size=0 vlen=2 > type_id=10 offset=0 size=4 (VAR 'multi_tag_var') > type_id=11 offset=0 size=4 (VAR 'global_var') > $ bpftool btf dump file decl_tag.o | grep DECL_TAG > [14] DECL_TAG 'global_var_tag' type_id=11 component_idx=-1 > [15] DECL_TAG 'static_var_tag' type_id=0 component_idx=-1 > [16] DECL_TAG 'tag_b' type_id=10 component_idx=-1 > [17] DECL_TAG 'tag_a' type_id=10 component_idx=-1 > [18] DECL_TAG 'member_a_tag' type_id=2 component_idx=0 > [19] DECL_TAG 'member_b_tag' type_id=2 component_idx=1 > [20] DECL_TAG 'param_x_tag' type_id=13 component_idx=0 > [21] DECL_TAG 'param_y_tag' type_id=13 component_idx=1 > [22] DECL_TAG 'func_add_tag' type_id=13 component_idx=-1 > $ > > Three decl tags (struct_foo_tag, typedef_foo_tag and typedef_foo2_tag) > are missing here: I'll double check, once I'm back from PTO. > struct foo { > int a __tag("member_a_tag"); > int b __tag("member_b_tag"); > } __tag("struct_foo_tag"); > > /* tag on a typedef */ > typedef struct foo foo_t1 __tag("typedef_foo_tag"); > typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag"); > > $ clang -O2 -g -target bpf -c decl_tag.c -o decl_tag.o > $ bpftool btf dump file decl_tag.o > [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED > [2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2 > 'x' type_id=1 > 'y' type_id=1 > [3] FUNC 'add' type_id=2 linkage=global > [4] DECL_TAG 'param_x_tag' type_id=3 component_idx=0 > [5] DECL_TAG 'param_y_tag' type_id=3 component_idx=1 > [6] DECL_TAG 'func_add_tag' type_id=3 component_idx=-1 > [7] PTR '(anon)' type_id=8 > [8] TYPEDEF 'foo_t1' type_id=10 > [9] DECL_TAG 'typedef_foo_tag' type_id=8 component_idx=-1 > [10] STRUCT 'foo' size=8 vlen=2 > 'a' type_id=1 bits_offset=0 > 'b' type_id=1 bits_offset=32 > [11] DECL_TAG 'struct_foo_tag' type_id=10 component_idx=-1 > [12] DECL_TAG 'member_a_tag' type_id=10 component_idx=0 > [13] DECL_TAG 'member_b_tag' type_id=10 component_idx=1 > [14] PTR '(anon)' type_id=15 > [15] TYPEDEF 'foo_t2' type_id=17 > [16] DECL_TAG 'typedef_foo2_tag' type_id=15 component_idx=-1 > [17] STRUCT '(anon)' size=4 vlen=1 > 'foo2' type_id=1 bits_offset=0 > [18] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2 > 'f' type_id=7 > 'g' type_id=14 > [19] FUNC 'use' type_id=18 linkage=global > [20] VAR 'global_var' type_id=1, linkage=global > [21] DECL_TAG 'global_var_tag' type_id=20 component_idx=-1 > [22] VAR 'multi_tag_var' type_id=1, linkage=global > [23] DECL_TAG 'tag_a' type_id=22 component_idx=-1 > [24] DECL_TAG 'tag_b' type_id=22 component_idx=-1 > [25] DATASEC '.bss' size=0 vlen=2 > type_id=20 offset=0 size=4 (VAR 'global_var') > type_id=22 offset=0 size=4 (VAR 'multi_tag_var') > $ bpftool btf dump file decl_tag.o | grep DECL_TAG > [4] DECL_TAG 'param_x_tag' type_id=3 component_idx=0 > [5] DECL_TAG 'param_y_tag' type_id=3 component_idx=1 > [6] DECL_TAG 'func_add_tag' type_id=3 component_idx=-1 > [9] DECL_TAG 'typedef_foo_tag' type_id=8 component_idx=-1 > [11] DECL_TAG 'struct_foo_tag' type_id=10 component_idx=-1 > [12] DECL_TAG 'member_a_tag' type_id=10 component_idx=0 > [13] DECL_TAG 'member_b_tag' type_id=10 component_idx=1 > [16] DECL_TAG 'typedef_foo2_tag' type_id=15 component_idx=-1 > [21] DECL_TAG 'global_var_tag' type_id=20 component_idx=-1 > [23] DECL_TAG 'tag_a' type_id=22 component_idx=-1 > [24] DECL_TAG 'tag_b' type_id=22 component_idx=-1 > > DECL_TAG 'static_var_tag' type_id=0 component_idx=-1 is missing in llvm. > This is execpted for llvm since 'static_var' is 'inlined' since it is > value is 0 and the compiler optimization removed it in function use(). > In llvm Dwarf->BTF conversion is very late and we only emit survived > globals. > > gcc emits 'static_var_tag' probably in frontend. Emitting > 'static_var_tag' is okay, just not used. > > I think gcc should support > - declaration tag for typedef, and > - declaration tag for the whole struct (like above 'struct_foo_tag') > to be compatible with llvm. OK we will look into this. Thx, -Vineet ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation 2026-06-03 20:54 ` Vineet Gupta @ 2026-06-03 21:40 ` Yonghong Song 0 siblings, 0 replies; 16+ messages in thread From: Yonghong Song @ 2026-06-03 21:40 UTC (permalink / raw) To: Vineet Gupta, dwarves Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis, jose.marchesi, David Faust On 6/3/26 1:54 PM, Vineet Gupta wrote: > Hi Yonghong, > > Thanks for reviewing the patches. > > On 6/3/26 1:08 PM, Yonghong Song wrote: >>> [3] >>> https://lore.kernel.org/bpf/20260601183511.594100-2-vineet.gupta@linux.dev/ >>> [2] >>> https://lore.kernel.org/bpf/20260528223616.2035618-2-vineet.gupta@linux.dev/ >>> [1] >>> https://lore.kernel.org/bpf/20260526181818.4159927-2-vineet.gupta@linux.dev/ >> I download and build gcc-16.1 which will be used for gcc compilation. >> I did some experiments with this patch set for both type tag and decl >> tag. >> See below. > > FWIW, you definitely need a fix [1] on top of gcc 16.1. > The patch explains the reasoning and did cause some missing entries in > type chain. > I haven't checked if this changes anything specific to issues you > point below. > > [1] https://gcc.gnu.org/pipermail/bpf/2026-May/000147.html Thanks for the pointer. The fix is related to type tag. So I suspect typedef and struct decl_tag support is not done yet in gcc. > > >> >> For type tag >> ============ >> >> $ cat type_tag.c >> /* btf_type_tag test cases. >> * >> * btf_type_tag annotates a *type* and the tag becomes part of the >> type >> * chain in BTF (TYPE_TAG kind), e.g. used by the kernel for __user >> / __rcu >> * / __percpu style annotations. >> * >> * Placement matters: the tag is written *before* the '*' so it >> annotates >> * the pointee type. This produces PTR -> TYPE_TAG 'x' -> <pointee>. >> * >> * Build: clang -O2 -g -target bpf -c type_tag.c -o type_tag.o >> * /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf >> -g -c type_tag.c -o type_tag.o >> * Dump: bpftool btf dump file type_tag.o >> */ >> >> #define __type_tag(x) __attribute__((btf_type_tag(x))) >> >> /* a single type tag on a pointer's pointee */ >> int __type_tag("user") *user_ptr_var; >> >> /* stacked type tags: the chain is preserved, outermost first */ >> int __type_tag("tag1") __type_tag("tag2") *stacked_var; >> >> /* type tags inside struct members */ >> struct bar { >> char __type_tag("rcu") *name; >> int __type_tag("percpu") *counter; >> }; >> >> /* type tag on a function parameter's pointee */ >> int read_user(int __type_tag("user") *p) >> { >> return *p; >> } >> >> /* keep things referenced */ >> int use(struct bar *b) >> { >> return read_user(user_ptr_var) + (stacked_var ? >> *stacked_var : 0) + >> (b->counter ? *b->counter : 0); >> } >> >> $ /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c >> type_tag.c -o type_tag.o >> $ bpftool btf dump file type_tag.o >> [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED >> [2] PTR '(anon)' type_id=15 >> [3] PTR '(anon)' type_id=17 >> [4] STRUCT 'bar' size=16 vlen=2 >> 'name' type_id=6 bits_offset=0 >> 'counter' type_id=7 bits_offset=64 >> [5] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED >> [6] PTR '(anon)' type_id=18 >> [7] PTR '(anon)' type_id=19 >> [8] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1 >> 'b' type_id=9 >> [9] PTR '(anon)' type_id=4 >> [10] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1 >> 'p' type_id=2 >> [11] VAR 'stacked_var' type_id=3, linkage=global >> [12] VAR 'user_ptr_var' type_id=2, linkage=global >> [13] FUNC 'use' type_id=8 linkage=global >> [14] FUNC 'read_user' type_id=10 linkage=global >> [15] TYPE_TAG 'user' type_id=1 >> [16] TYPE_TAG 'tag1' type_id=1 >> [17] TYPE_TAG 'tag2' type_id=16 >> [18] TYPE_TAG 'rcu' type_id=5 >> [19] TYPE_TAG 'percpu' type_id=1 >> [20] DATASEC '.bss' size=0 vlen=2 >> type_id=11 offset=0 size=8 (VAR 'stacked_var') >> type_id=12 offset=0 size=8 (VAR 'user_ptr_var') >> $ >> >> $ clang -O2 -g -target bpf -c type_tag.c -o type_tag.o >> $ bpftool btf dump file type_tag.o >> [1] TYPE_TAG 'user' type_id=3 >> [2] PTR '(anon)' type_id=1 >> [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED >> [4] FUNC_PROTO '(anon)' ret_type_id=3 vlen=1 >> 'p' type_id=2 >> [5] FUNC 'read_user' type_id=4 linkage=global >> [6] PTR '(anon)' type_id=7 >> [7] STRUCT 'bar' size=16 vlen=2 >> 'name' type_id=9 bits_offset=0 >> 'counter' type_id=12 bits_offset=64 >> [8] TYPE_TAG 'rcu' type_id=10 >> [9] PTR '(anon)' type_id=8 >> [10] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED >> [11] TYPE_TAG 'percpu' type_id=3 >> [12] PTR '(anon)' type_id=11 >> [13] FUNC_PROTO '(anon)' ret_type_id=3 vlen=1 >> 'b' type_id=6 >> [14] FUNC 'use' type_id=13 linkage=global >> [15] VAR 'user_ptr_var' type_id=2, linkage=global >> [16] TYPE_TAG 'tag1' type_id=3 >> [17] TYPE_TAG 'tag2' type_id=16 >> [18] PTR '(anon)' type_id=17 >> [19] VAR 'stacked_var' type_id=18, linkage=global >> [20] DATASEC '.bss' size=0 vlen=2 >> type_id=15 offset=0 size=8 (VAR 'user_ptr_var') >> type_id=19 offset=0 size=8 (VAR 'stacked_var') >> $ >> >> So type tag matches between clang and gcc. > > Nice, thanks for cross-checking and confirming. > > >> For decl tag >> ============ >> >> $ cat decl_tag.c >> /* btf_decl_tag test cases. >> * >> * btf_decl_tag can be attached to: >> * - global (incl. static) variables >> * - functions >> * - function parameters >> * - struct/union types and their members >> * - typedefs >> * >> * Build: clang -O2 -target bpf -g -c decl_tag.c -o decl_tag.o >> * /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf >> -g -c decl_tag.c -o decl_tag.o >> * Dump: bpftool btf dump file decl_tag.o >> */ >> >> #define __tag(x) __attribute__((btf_decl_tag(x))) >> >> /* tag on a global variable */ >> int global_var __tag("global_var_tag"); >> >> /* tag on a static variable */ >> static int static_var __tag("static_var_tag"); >> >> /* multiple tags on one declaration */ >> int multi_tag_var __tag("tag_a") __tag("tag_b"); >> >> /* tag on struct type and its members */ >> struct foo { >> int a __tag("member_a_tag"); >> int b __tag("member_b_tag"); >> } __tag("struct_foo_tag"); >> >> /* tag on a typedef */ >> typedef struct foo foo_t1 __tag("typedef_foo_tag"); >> typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag"); >> >> /* tag on a function and its parameters */ >> __tag("func_add_tag") >> int add(int x __tag("param_x_tag"), int y __tag("param_y_tag")) >> { >> return x + y; >> } >> >> /* keep the globals/types alive so they land in BTF */ >> int use(foo_t1 *f, foo_t2 *g) >> { >> return add(global_var + static_var + multi_tag_var, f->a + >> g->foo2); >> } >> >> $ /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c >> decl_tag.c -o decl_tag.o >> decl_tag.c:30:1: warning: ‘btf_decl_tag’ attribute does not apply to >> types [-Wattributes] >> 30 | } __tag("struct_foo_tag"); >> | ^ >> >> $ bpftool btf dump file decl_tag.o >> [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED >> [2] STRUCT 'foo' size=8 vlen=2 >> 'a' type_id=1 bits_offset=0 >> 'b' type_id=1 bits_offset=32 >> [3] TYPEDEF 'foo_t1' type_id=2 >> [4] STRUCT '(anon)' size=4 vlen=1 >> 'foo2' type_id=1 bits_offset=0 >> [5] TYPEDEF 'foo_t2' type_id=4 >> [6] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2 >> 'f' type_id=7 >> 'g' type_id=8 >> [7] PTR '(anon)' type_id=3 >> [8] PTR '(anon)' type_id=5 >> [9] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2 >> 'x' type_id=1 >> 'y' type_id=1 >> [10] VAR 'multi_tag_var' type_id=1, linkage=global >> [11] VAR 'global_var' type_id=1, linkage=global >> [12] FUNC 'use' type_id=6 linkage=global >> [13] FUNC 'add' type_id=9 linkage=global >> [14] DECL_TAG 'global_var_tag' type_id=11 component_idx=-1 >> [15] DECL_TAG 'static_var_tag' type_id=0 component_idx=-1 >> [16] DECL_TAG 'tag_b' type_id=10 component_idx=-1 >> [17] DECL_TAG 'tag_a' type_id=10 component_idx=-1 >> [18] DECL_TAG 'member_a_tag' type_id=2 component_idx=0 >> [19] DECL_TAG 'member_b_tag' type_id=2 component_idx=1 >> [20] DECL_TAG 'param_x_tag' type_id=13 component_idx=0 >> [21] DECL_TAG 'param_y_tag' type_id=13 component_idx=1 >> [22] DECL_TAG 'func_add_tag' type_id=13 component_idx=-1 >> [23] DATASEC '.bss' size=0 vlen=2 >> type_id=10 offset=0 size=4 (VAR 'multi_tag_var') >> type_id=11 offset=0 size=4 (VAR 'global_var') >> $ bpftool btf dump file decl_tag.o | grep DECL_TAG >> [14] DECL_TAG 'global_var_tag' type_id=11 component_idx=-1 >> [15] DECL_TAG 'static_var_tag' type_id=0 component_idx=-1 >> [16] DECL_TAG 'tag_b' type_id=10 component_idx=-1 >> [17] DECL_TAG 'tag_a' type_id=10 component_idx=-1 >> [18] DECL_TAG 'member_a_tag' type_id=2 component_idx=0 >> [19] DECL_TAG 'member_b_tag' type_id=2 component_idx=1 >> [20] DECL_TAG 'param_x_tag' type_id=13 component_idx=0 >> [21] DECL_TAG 'param_y_tag' type_id=13 component_idx=1 >> [22] DECL_TAG 'func_add_tag' type_id=13 component_idx=-1 >> $ >> >> Three decl tags (struct_foo_tag, typedef_foo_tag and typedef_foo2_tag) >> are missing here: > > I'll double check, once I'm back from PTO. > >> struct foo { >> int a __tag("member_a_tag"); >> int b __tag("member_b_tag"); >> } __tag("struct_foo_tag"); >> >> /* tag on a typedef */ >> typedef struct foo foo_t1 __tag("typedef_foo_tag"); >> typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag"); >> >> $ clang -O2 -g -target bpf -c decl_tag.c -o decl_tag.o >> $ bpftool btf dump file decl_tag.o >> [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED >> [2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2 >> 'x' type_id=1 >> 'y' type_id=1 >> [3] FUNC 'add' type_id=2 linkage=global >> [4] DECL_TAG 'param_x_tag' type_id=3 component_idx=0 >> [5] DECL_TAG 'param_y_tag' type_id=3 component_idx=1 >> [6] DECL_TAG 'func_add_tag' type_id=3 component_idx=-1 >> [7] PTR '(anon)' type_id=8 >> [8] TYPEDEF 'foo_t1' type_id=10 >> [9] DECL_TAG 'typedef_foo_tag' type_id=8 component_idx=-1 >> [10] STRUCT 'foo' size=8 vlen=2 >> 'a' type_id=1 bits_offset=0 >> 'b' type_id=1 bits_offset=32 >> [11] DECL_TAG 'struct_foo_tag' type_id=10 component_idx=-1 >> [12] DECL_TAG 'member_a_tag' type_id=10 component_idx=0 >> [13] DECL_TAG 'member_b_tag' type_id=10 component_idx=1 >> [14] PTR '(anon)' type_id=15 >> [15] TYPEDEF 'foo_t2' type_id=17 >> [16] DECL_TAG 'typedef_foo2_tag' type_id=15 component_idx=-1 >> [17] STRUCT '(anon)' size=4 vlen=1 >> 'foo2' type_id=1 bits_offset=0 >> [18] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2 >> 'f' type_id=7 >> 'g' type_id=14 >> [19] FUNC 'use' type_id=18 linkage=global >> [20] VAR 'global_var' type_id=1, linkage=global >> [21] DECL_TAG 'global_var_tag' type_id=20 component_idx=-1 >> [22] VAR 'multi_tag_var' type_id=1, linkage=global >> [23] DECL_TAG 'tag_a' type_id=22 component_idx=-1 >> [24] DECL_TAG 'tag_b' type_id=22 component_idx=-1 >> [25] DATASEC '.bss' size=0 vlen=2 >> type_id=20 offset=0 size=4 (VAR 'global_var') >> type_id=22 offset=0 size=4 (VAR 'multi_tag_var') >> $ bpftool btf dump file decl_tag.o | grep DECL_TAG >> [4] DECL_TAG 'param_x_tag' type_id=3 component_idx=0 >> [5] DECL_TAG 'param_y_tag' type_id=3 component_idx=1 >> [6] DECL_TAG 'func_add_tag' type_id=3 component_idx=-1 >> [9] DECL_TAG 'typedef_foo_tag' type_id=8 component_idx=-1 >> [11] DECL_TAG 'struct_foo_tag' type_id=10 component_idx=-1 >> [12] DECL_TAG 'member_a_tag' type_id=10 component_idx=0 >> [13] DECL_TAG 'member_b_tag' type_id=10 component_idx=1 >> [16] DECL_TAG 'typedef_foo2_tag' type_id=15 component_idx=-1 >> [21] DECL_TAG 'global_var_tag' type_id=20 component_idx=-1 >> [23] DECL_TAG 'tag_a' type_id=22 component_idx=-1 >> [24] DECL_TAG 'tag_b' type_id=22 component_idx=-1 >> >> DECL_TAG 'static_var_tag' type_id=0 component_idx=-1 is missing in llvm. >> This is execpted for llvm since 'static_var' is 'inlined' since it is >> value is 0 and the compiler optimization removed it in function use(). >> In llvm Dwarf->BTF conversion is very late and we only emit survived >> globals. >> >> gcc emits 'static_var_tag' probably in frontend. Emitting >> 'static_var_tag' is okay, just not used. >> >> I think gcc should support >> - declaration tag for typedef, and >> - declaration tag for the whole struct (like above 'struct_foo_tag') >> to be compatible with llvm. > > OK we will look into this. Thanks! > > Thx, > -Vineet > ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation 2026-06-03 20:08 ` Yonghong Song 2026-06-03 20:54 ` Vineet Gupta @ 2026-06-17 18:18 ` Vineet Gupta 1 sibling, 0 replies; 16+ messages in thread From: Vineet Gupta @ 2026-06-17 18:18 UTC (permalink / raw) To: Yonghong Song, dwarves Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis, jose.marchesi, David Faust On 6/3/26 1:08 PM, Yonghong Song wrote: > I download and build gcc-16.1 which will be used for gcc compilation. > I did some experiments with this patch set for both type tag and decl tag. Thx for this doing this exercise. > For type tag > ============ [snip] > So type tag matches between clang and gcc. Nice ! > For decl tag > ============ > > $ cat decl_tag.c > /* btf_decl_tag test cases. > * > * btf_decl_tag can be attached to: > * - global (incl. static) variables > * - functions > * - function parameters > * - struct/union types and their members > * - typedefs > * > * Build: clang -O2 -target bpf -g -c decl_tag.c -o decl_tag.o > * /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c decl_tag.c -o decl_tag.o > * Dump: bpftool btf dump file decl_tag.o > */ > > #define __tag(x) __attribute__((btf_decl_tag(x))) > > /* tag on a global variable */ > int global_var __tag("global_var_tag"); > > /* tag on a static variable */ > static int static_var __tag("static_var_tag"); > > /* multiple tags on one declaration */ > int multi_tag_var __tag("tag_a") __tag("tag_b"); > > /* tag on struct type and its members */ > struct foo { > int a __tag("member_a_tag"); > int b __tag("member_b_tag"); > } __tag("struct_foo_tag"); > > /* tag on a typedef */ > typedef struct foo foo_t1 __tag("typedef_foo_tag"); > typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag"); > > /* tag on a function and its parameters */ > __tag("func_add_tag") > int add(int x __tag("param_x_tag"), int y __tag("param_y_tag")) > { > return x + y; > } > > /* keep the globals/types alive so they land in BTF */ > int use(foo_t1 *f, foo_t2 *g) > { > return add(global_var + static_var + multi_tag_var, f->a + g->foo2); > } > > $ /home/yhs/work/gcc-build/opt/gcc-16.1/bin/gcc -O2 -gbtf -g -c decl_tag.c -o decl_tag.o > decl_tag.c:30:1: warning: ‘btf_decl_tag’ attribute does not apply to types [-Wattributes] > 30 | } __tag("struct_foo_tag"); > | ^ > [snip] > Three decl tags (struct_foo_tag, typedef_foo_tag and typedef_foo2_tag) > are missing here: > > struct foo { > int a __tag("member_a_tag"); > int b __tag("member_b_tag"); > } __tag("struct_foo_tag"); > > /* tag on a typedef */ > typedef struct foo foo_t1 __tag("typedef_foo_tag"); > typedef struct {int foo2;} foo_t2 __tag("typedef_foo2_tag"); Semantically what does this mean ? Will the decl tag will be "applied" where ever the type is instantiated ? > /* tag on a static variable */ > static int static_var __tag("static_var_tag"); > > ... > > [24] DECL_TAG 'tag_b' type_id=22 component_idx=-1 > > DECL_TAG 'static_var_tag' type_id=0 component_idx=-1 is missing in llvm. > This is execpted for llvm since 'static_var' is 'inlined' since it is > value is 0 and the compiler optimization removed it in function use(). > In llvm Dwarf->BTF conversion is very late and we only emit survived > globals. > > gcc emits 'static_var_tag' probably in frontend. Emitting > 'static_var_tag' is okay, just not used. Yeah I recall something like this when talking to David. We can live with this it seems. > I think gcc should support > - declaration tag for typedef, and > - declaration tag for the whole struct (like above 'struct_foo_tag') > to be compatible with llvm. OK, opened PR/125862 [1] for future improvement. [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125862 Thx, -Vineet ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation 2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta 2026-06-03 20:08 ` Yonghong Song @ 2026-06-03 20:42 ` Emil Tsalapatis 2026-06-03 21:41 ` Yonghong Song 2026-06-07 9:54 ` Alan Maguire 3 siblings, 0 replies; 16+ messages in thread From: Emil Tsalapatis @ 2026-06-03 20:42 UTC (permalink / raw) To: Vineet Gupta, dwarves Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis, jose.marchesi, David Faust On Tue Jun 2, 2026 at 3:55 PM EDT, Vineet Gupta wrote: > gcc 16 was the first release to support DW_TAG_GNU_annotations and this > patch enables the same in pahole. Bulk of changes are dwarf_loader but > btf_encoder also gains support with minimal changes. > > GCC encodes btf_type_tag and btf_decl_tag annotations differently from > LLVM. While LLVM uses DW_TAG_LLVM_annotation (0x6000) as child DIEs, > GCC uses DW_TAG_GNU_annotation (0x6001) as standalone sibling DIEs > referenced via DW_AT_GNU_annotation (0x2139) attributes, with chaining > through the same attribute on annotation DIEs themselves. > > Handle both encoding styles: > > For btf_type_tag (pointer annotations): > - Recognize DW_TAG_GNU_annotation alongside DW_TAG_LLVM_annotation in > child annotation scanning. > - Follow DW_AT_GNU_annotation attribute chains on pointer types for > GCC-style btf_type_tag resolution. > - Normalize DW_TAG_GNU_annotation to DW_TAG_LLVM_annotation in the > internal representation so downstream code works unchanged. > > For btf_decl_tag (function/struct/member annotations): > - Add add_gnu_annotation_chain() to follow DW_AT_GNU_annotation > attribute chains on function, struct, and member DIEs. > - GCC puts DW_AT_GNU_annotation on the function/struct DIE itself > (not as child DIEs), referencing sibling annotation DIEs that chain > via the same attribute. > > Also: > - Silently skip standalone DW_TAG_GNU_annotation DIEs at CU level. > - Add tag__is_annotation() helper macro for annotation tag checks. > - Rename add_llvm_annotation -> add_tag_annotation, > skip_llvm_annotations -> skip_tag_annotations since these now > handle both LLVM and GNU annotation formats. > > Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com> One thing, we accept for both LLVM and GCC annotation tags being used as LLVM annotation tags because we check against tags__is_annotation(). This is valid imo. > --- > Changes since v3 [3] > - Add helper tag__is_annotation [Emil] > - Rename some functions containing "llvm" as they are common to LLVM/GCC tags [Emil] > - Deduplicate checks before loop and inside loop in add_gnu_annotation_chain() and check_gnu_attr [Arnaldo] > - Fix some typos and move some comments [Emi] > > Changes since v2 [2] > - Removed loop detection logic [Alan] > - Move test changes to different patch [Alan] > > Changes since v1 [1] > - NFC Reduce indentation with early exits (Alexei offlist) > > [3] https://lore.kernel.org/bpf/20260601183511.594100-2-vineet.gupta@linux.dev/ > [2] https://lore.kernel.org/bpf/20260528223616.2035618-2-vineet.gupta@linux.dev/ > [1] https://lore.kernel.org/bpf/20260526181818.4159927-2-vineet.gupta@linux.dev/ > --- > btf_encoder.c | 1 + > dutil.h | 11 +++++ > dwarf_loader.c | 105 +++++++++++++++++++++++++++++++++++++++------- > dwarves.h | 2 +- > dwarves_fprintf.c | 12 ++++-- > 5 files changed, 110 insertions(+), 21 deletions(-) > > diff --git a/btf_encoder.c b/btf_encoder.c > index 633bc6162ce0..d5af706d7638 100644 > --- a/btf_encoder.c > +++ b/btf_encoder.c > @@ -1831,6 +1831,7 @@ static int btf_encoder__encode_tag(struct btf_encoder *encoder, struct tag *tag, > name = namespace__name(tag__namespace(tag)); > return btf_encoder__add_ref_type(encoder, BTF_KIND_TYPEDEF, ref_type_id, name, false); > case DW_TAG_LLVM_annotation: > + case DW_TAG_GNU_annotation: > name = tag__btf_type_tag(tag)->value; > return btf_encoder__add_ref_type(encoder, BTF_KIND_TYPE_TAG, ref_type_id, name, false); > case DW_TAG_structure_type: > diff --git a/dutil.h b/dutil.h > index ff78aa6dfd10..be02c68cf4a7 100644 > --- a/dutil.h > +++ b/dutil.h > @@ -35,6 +35,17 @@ > #define DW_TAG_LLVM_annotation 0x6000 > #endif > > +#ifndef DW_TAG_GNU_annotation > +#define DW_TAG_GNU_annotation 0x6001 > +#endif > + > +#ifndef DW_AT_GNU_annotation > +#define DW_AT_GNU_annotation 0x2139 > +#endif > + > +#define tag__is_annotation(tag) \ > + ((tag) == DW_TAG_LLVM_annotation || (tag) == DW_TAG_GNU_annotation) > + > static inline __attribute__((const)) bool is_power_of_2(unsigned long n) > { > return (n != 0 && ((n & (n - 1)) == 0)); > diff --git a/dwarf_loader.c b/dwarf_loader.c > index 42a0f3f74ce3..14f71c9043aa 100644 > --- a/dwarf_loader.c > +++ b/dwarf_loader.c > @@ -908,7 +908,12 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b > return -ENOMEM; > } > > -static int add_llvm_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf, > +static bool die__tag_is_annotation(Dwarf_Die *die) > +{ > + return tag__is_annotation(dwarf_tag(die)); > +} > + > +static int add_tag_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf, > struct list_head *head) > { > struct llvm_annotation *annot; > @@ -943,16 +948,40 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx, > > die = &child; > do { > - if (dwarf_tag(die) == DW_TAG_LLVM_annotation) { > - ret = add_llvm_annotation(die, component_idx, conf, head); > - if (ret) > - return ret; > - } > + if (!die__tag_is_annotation(die)) > + continue; > + > + ret = add_tag_annotation(die, component_idx, conf, head); > + if (ret) > + return ret; > } while (dwarf_siblingof(die, die) == 0); > > return 0; > } > > +/* > + * Handle gcc style btf_decl_tag annotations for functions/struct/member tags. > + * Pointers are handled separately, inline in die__create_new_pointer_tag() > + */ > +static int add_gnu_annotation_chain(Dwarf_Die *die, int component_idx, > + struct conf_load *conf, struct list_head *head) > +{ > + Dwarf_Attribute attr; > + Dwarf_Die annot_die; > + > + while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL && > + dwarf_formref_die(&attr, &annot_die) != NULL && > + dwarf_tag(&annot_die) == DW_TAG_GNU_annotation) { > + int ret = add_tag_annotation(&annot_die, component_idx, conf, head); > + if (ret) > + return ret; > + > + die = &annot_die; > + } > + > + return 0; > +} > + > int class_member__dwarf_recode_bitfield(struct class_member *member, > struct cu *cu) > { > @@ -1596,6 +1625,8 @@ static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *di > return NULL; > > tag__init(&tag->tag, cu, die); > + /* Normalize DW_TAG_GNU_annotation to DW_TAG_LLVM_annotation internally */ > + tag->tag.tag = DW_TAG_LLVM_annotation; > tag->value = attr_string(die, DW_AT_const_value, conf); > return tag; > } > @@ -1637,19 +1668,21 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu, > { > struct btf_type_tag_ptr_type *tag = NULL; > Dwarf_Die *cdie, child; > + Dwarf_Attribute attr; > + Dwarf_Die annot_die; > const char *name; > > - /* If no child tags or skipping btf_type_tag encoding, just create a new tag > - * and return > - */ > - if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0 || > - conf->skip_encoding_btf_type_tag) > + /* If skipping btf_type_tag encoding, just create a new tag, return */ > + if (conf->skip_encoding_btf_type_tag) > return tag__new(die, cu); > > - /* Otherwise, check DW_TAG_LLVM_annotation child tags */ > + if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0) > + goto check_gnu_attr; > + > + /* Handle LLVM style annotation tags if present */ > cdie = &child; > do { > - if (dwarf_tag(cdie) != DW_TAG_LLVM_annotation) > + if (!die__tag_is_annotation(cdie)) > continue; > > /* Only check btf_type_tag annotations */ > @@ -1662,6 +1695,26 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu, > return NULL; > } while (dwarf_siblingof(cdie, cdie) == 0); > > +check_gnu_attr: > + if (tag != NULL) > + goto out; > + > + /* Handle GCC-style DW_AT_GNU_annotation attribute */ > + while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL && > + dwarf_formref_die(&attr, &annot_die) != NULL && > + dwarf_tag(&annot_die) == DW_TAG_GNU_annotation) { > + name = attr_string(&annot_die, DW_AT_name, conf); > + if (strcmp(name, "btf_type_tag") != 0) > + break; > + > + tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf); > + if (tag == NULL) > + return NULL; > + > + die = &annot_die; > + } > + > +out: > return tag ? &tag->tag : tag__new(die, cu); > } > > @@ -1690,6 +1743,12 @@ static struct tag *die__create_new_class(Dwarf_Die *die, struct cu *cu, struct c > } > } > > + if (class != NULL && > + add_gnu_annotation_chain(die, -1, conf, &class->type.namespace.annots) != 0) { > + class__delete(class, cu); > + class = NULL; > + } > + > return class ? &class->type.namespace.tag : NULL; > } > > @@ -2051,11 +2110,14 @@ static int die__process_class(Dwarf_Die *die, struct type *class, > cu__hash(cu, &member->tag); > if (add_child_llvm_annotations(die, member_idx, conf, &class->namespace.annots)) > return -ENOMEM; > + if (add_gnu_annotation_chain(die, member_idx, conf, &class->namespace.annots)) > + return -ENOMEM; > member_idx++; > } > continue; > case DW_TAG_LLVM_annotation: > - if (add_llvm_annotation(die, -1, conf, &class->namespace.annots)) > + case DW_TAG_GNU_annotation: > + if (add_tag_annotation(die, -1, conf, &class->namespace.annots)) > return -ENOMEM; > continue; > default: { > @@ -2360,7 +2422,8 @@ static int die__process_function(Dwarf_Die *die, struct ftype *ftype, > goto out_enomem; > continue; > case DW_TAG_LLVM_annotation: > - if (add_llvm_annotation(die, -1, conf, &(tag__function(&ftype->tag)->annots))) > + case DW_TAG_GNU_annotation: > + if (add_tag_annotation(die, -1, conf, &(tag__function(&ftype->tag)->annots))) > goto out_enomem; > continue; > default: > @@ -2408,6 +2471,12 @@ static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *cu, struc > function = NULL; > } > > + if (function != NULL && > + add_gnu_annotation_chain(die, -1, conf, &function->annots) != 0) { > + function__delete(function, cu); > + function = NULL; > + } > + > return function ? &function->proto.tag : NULL; > } > > @@ -2469,6 +2538,9 @@ static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu, > */ > tag = &unsupported_tag; > break; > + case DW_TAG_GNU_annotation: > + tag = &unsupported_tag; > + break; > case DW_TAG_label: > if (conf->ignore_labels) > tag = &unsupported_tag; // callers will assume conf->ignore_labels is true > @@ -2494,7 +2566,8 @@ static int die__process_unit(Dwarf_Die *die, struct cu *cu, struct conf_load *co > // XXX special case DW_TAG_dwarf_procedure, appears when looking at a recent ~/bin/perf > // Investigate later how to properly support this... > if (dwarf_tag(die) != DW_TAG_dwarf_procedure && > - dwarf_tag(die) != DW_TAG_label) // conf->ignore_labels == true, see die__process_tag() > + dwarf_tag(die) != DW_TAG_label && // conf->ignore_labels == true, see die__process_tag() > + dwarf_tag(die) != DW_TAG_GNU_annotation) > tag__print_not_supported(die); > continue; > } > diff --git a/dwarves.h b/dwarves.h > index 5ec16e750e83..75c311a2f8ee 100644 > --- a/dwarves.h > +++ b/dwarves.h > @@ -670,7 +670,7 @@ static inline int tag__is_tag_type(const struct tag *tag) > tag->tag == DW_TAG_volatile_type || > tag->tag == DW_TAG_atomic_type || > tag->tag == DW_TAG_unspecified_type || > - tag->tag == DW_TAG_LLVM_annotation; > + tag__is_annotation(tag->tag); > } > > static inline const char *tag__decl_file(const struct tag *tag, > diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c > index 1ec478c2a027..757e4992a1f7 100644 > --- a/dwarves_fprintf.c > +++ b/dwarves_fprintf.c > @@ -140,6 +140,8 @@ const char *dwarf_tag_name(const uint32_t tag) > return dwarf_gnu_tag_names[tag - DW_TAG_MIPS_loop]; > else if (tag == DW_TAG_LLVM_annotation) > return "LLVM_annotation"; > + else if (tag == DW_TAG_GNU_annotation) > + return "GNU_annotation"; > return "INVALID"; > } > > @@ -658,6 +660,7 @@ static const char *__tag__name(const struct tag *tag, const struct cu *cu, > snprintf(bf, len, "%s", variable__name(tag__variable(tag))); > break; > case DW_TAG_LLVM_annotation: > + case DW_TAG_GNU_annotation: > type = cu__type(cu, tag->type); > if (type == NULL && tag->type != 0) > tag__id_not_found_snprintf(bf, len, tag->type); > @@ -723,7 +726,7 @@ static size_t type__fprintf_stats(struct type *type, const struct cu *cu, > return printed; > } > > -static type_id_t skip_llvm_annotations(const struct cu *cu, type_id_t id) > +static type_id_t skip_tag_annotations(const struct cu *cu, type_id_t id) > { > struct tag *type; > > @@ -731,7 +734,7 @@ static type_id_t skip_llvm_annotations(const struct cu *cu, type_id_t id) > if (id == 0) > break; > type = cu__type(cu, id); > - if (type == NULL || type->tag != DW_TAG_LLVM_annotation || type->type == id) > + if (type == NULL || !tag__is_annotation(type->tag) || type->type == id) > break; > id = type->type; > } > @@ -838,7 +841,7 @@ inner_struct: > next_type: > switch (type->tag) { > case DW_TAG_pointer_type: { > - type_id_t ptype_id = skip_llvm_annotations(cu, type->type); > + type_id_t ptype_id = skip_tag_annotations(cu, type->type); > > if (ptype_id != 0) { > int n; > @@ -936,7 +939,8 @@ print_modifier: { > else > printed += enumeration__fprintf(type, &tconf, fp); > break; > - case DW_TAG_LLVM_annotation: { > + case DW_TAG_LLVM_annotation: > + case DW_TAG_GNU_annotation: { > struct tag *ttype = cu__type(cu, type->type); > if (ttype) { > type = ttype; ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation 2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta 2026-06-03 20:08 ` Yonghong Song 2026-06-03 20:42 ` Emil Tsalapatis @ 2026-06-03 21:41 ` Yonghong Song 2026-06-17 18:34 ` Vineet Gupta 2026-06-07 9:54 ` Alan Maguire 3 siblings, 1 reply; 16+ messages in thread From: Yonghong Song @ 2026-06-03 21:41 UTC (permalink / raw) To: Vineet Gupta, dwarves Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis, jose.marchesi, David Faust On 6/2/26 12:55 PM, Vineet Gupta wrote: > gcc 16 was the first release to support DW_TAG_GNU_annotations and this > patch enables the same in pahole. Bulk of changes are dwarf_loader but > btf_encoder also gains support with minimal changes. > > GCC encodes btf_type_tag and btf_decl_tag annotations differently from > LLVM. While LLVM uses DW_TAG_LLVM_annotation (0x6000) as child DIEs, > GCC uses DW_TAG_GNU_annotation (0x6001) as standalone sibling DIEs > referenced via DW_AT_GNU_annotation (0x2139) attributes, with chaining > through the same attribute on annotation DIEs themselves. > > Handle both encoding styles: > > For btf_type_tag (pointer annotations): > - Recognize DW_TAG_GNU_annotation alongside DW_TAG_LLVM_annotation in > child annotation scanning. > - Follow DW_AT_GNU_annotation attribute chains on pointer types for > GCC-style btf_type_tag resolution. > - Normalize DW_TAG_GNU_annotation to DW_TAG_LLVM_annotation in the > internal representation so downstream code works unchanged. > > For btf_decl_tag (function/struct/member annotations): > - Add add_gnu_annotation_chain() to follow DW_AT_GNU_annotation > attribute chains on function, struct, and member DIEs. > - GCC puts DW_AT_GNU_annotation on the function/struct DIE itself > (not as child DIEs), referencing sibling annotation DIEs that chain > via the same attribute. > > Also: > - Silently skip standalone DW_TAG_GNU_annotation DIEs at CU level. > - Add tag__is_annotation() helper macro for annotation tag checks. > - Rename add_llvm_annotation -> add_tag_annotation, > skip_llvm_annotations -> skip_tag_annotations since these now > handle both LLVM and GNU annotation formats. > > Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev> LGTM except a nit below. Acked-by: Yonghong Song <yonghong.song@linux.dev> [...] > @@ -943,16 +948,40 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx, > > die = &child; > do { > - if (dwarf_tag(die) == DW_TAG_LLVM_annotation) { > - ret = add_llvm_annotation(die, component_idx, conf, head); > - if (ret) > - return ret; > - } > + if (!die__tag_is_annotation(die)) > + continue; > + > + ret = add_tag_annotation(die, component_idx, conf, head); > + if (ret) > + return ret; > } while (dwarf_siblingof(die, die) == 0); > > return 0; > } > > +/* > + * Handle gcc style btf_decl_tag annotations for functions/struct/member tags. For 'struct', gcc is not supported yet. > + * Pointers are handled separately, inline in die__create_new_pointer_tag() > + */ > +static int add_gnu_annotation_chain(Dwarf_Die *die, int component_idx, > + struct conf_load *conf, struct list_head *head) > +{ > + Dwarf_Attribute attr; > + Dwarf_Die annot_die; > + > + while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL && > + dwarf_formref_die(&attr, &annot_die) != NULL && > + dwarf_tag(&annot_die) == DW_TAG_GNU_annotation) { > + int ret = add_tag_annotation(&annot_die, component_idx, conf, head); > + if (ret) > + return ret; > + > + die = &annot_die; > + } > + > + return 0; > +} > + > [...] ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation 2026-06-03 21:41 ` Yonghong Song @ 2026-06-17 18:34 ` Vineet Gupta 0 siblings, 0 replies; 16+ messages in thread From: Vineet Gupta @ 2026-06-17 18:34 UTC (permalink / raw) To: Yonghong Song, dwarves Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis, jose.marchesi, David Faust On 6/3/26 2:41 PM, Yonghong Song wrote: > LGTM except a nit below. > > Acked-by: Yonghong Song <yonghong.song@linux.dev> Thx ! >> + * Handle gcc style btf_decl_tag annotations for functions/struct/member tags. > For 'struct', gcc is not supported yet. Good catch, will fix in respin. Thx, -Vineet ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation 2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta ` (2 preceding siblings ...) 2026-06-03 21:41 ` Yonghong Song @ 2026-06-07 9:54 ` Alan Maguire 2026-06-17 20:08 ` Vineet Gupta 3 siblings, 1 reply; 16+ messages in thread From: Alan Maguire @ 2026-06-07 9:54 UTC (permalink / raw) To: Vineet Gupta, dwarves Cc: bpf, Andrii Nakryiko, acme, Emil Tsalapatis, jose.marchesi, David Faust On 02/06/2026 20:55, Vineet Gupta wrote: > gcc 16 was the first release to support DW_TAG_GNU_annotations and this > patch enables the same in pahole. Bulk of changes are dwarf_loader but > btf_encoder also gains support with minimal changes. > > GCC encodes btf_type_tag and btf_decl_tag annotations differently from > LLVM. While LLVM uses DW_TAG_LLVM_annotation (0x6000) as child DIEs, > GCC uses DW_TAG_GNU_annotation (0x6001) as standalone sibling DIEs > referenced via DW_AT_GNU_annotation (0x2139) attributes, with chaining > through the same attribute on annotation DIEs themselves. > > Handle both encoding styles: > > For btf_type_tag (pointer annotations): > - Recognize DW_TAG_GNU_annotation alongside DW_TAG_LLVM_annotation in > child annotation scanning. > - Follow DW_AT_GNU_annotation attribute chains on pointer types for > GCC-style btf_type_tag resolution. > - Normalize DW_TAG_GNU_annotation to DW_TAG_LLVM_annotation in the > internal representation so downstream code works unchanged. > > For btf_decl_tag (function/struct/member annotations): > - Add add_gnu_annotation_chain() to follow DW_AT_GNU_annotation > attribute chains on function, struct, and member DIEs. > - GCC puts DW_AT_GNU_annotation on the function/struct DIE itself > (not as child DIEs), referencing sibling annotation DIEs that chain > via the same attribute. > > Also: > - Silently skip standalone DW_TAG_GNU_annotation DIEs at CU level. > - Add tag__is_annotation() helper macro for annotation tag checks. > - Rename add_llvm_annotation -> add_tag_annotation, > skip_llvm_annotations -> skip_tag_annotations since these now > handle both LLVM and GNU annotation formats. > > Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev> Thanks for the respin! Sorry for the delay took me a while to get set up with latest gcc. Two things below, but in summary there are a few things we need to line things up between clang and gcc representations I think. To test these completely there are some existing btf_loader/pfunct issues below that I've outlined; I can send patches for those fixes as prerequisites, or feel free to roll into a respin, whatever you'd prefer. > --- > Changes since v3 [3] > - Add helper tag__is_annotation [Emil] > - Rename some functions containing "llvm" as they are common to LLVM/GCC tags [Emil] > - Deduplicate checks before loop and inside loop in add_gnu_annotation_chain() and check_gnu_attr [Arnaldo] > - Fix some typos and move some comments [Emi] > > Changes since v2 [2] > - Removed loop detection logic [Alan] > - Move test changes to different patch [Alan] > I don't want to introduce scope creep - and I may be missing something - but it seems like the current changes don't cover decl tags for function parameters. On this topic, there is an existing bug where decl tags that have a component_idx reference are not rendered correctly by pfunct which makes testing that properly impossible today. Parameter decl tags are prepended to the function declaration rather than tied to the parameter referenced by the component_idx, so we should fix that first. The root cause is btf_loader.c always attaches attributes to the function, whereas it should use the component_idx to attach them to the parameter tag where component_idx >= 0. The following changes are needed to fix that I think: diff --git a/btf_loader.c b/btf_loader.c index b591219..9bb52c3 100644 --- a/btf_loader.c +++ b/btf_loader.c @@ -477,12 +477,56 @@ static struct attributes *attributes__realloc(struct attributes *attributes, con return result; } +static struct tag *ftype__parameter(const struct ftype *ftype, int component_idx) +{ + struct parameter *pos; + int idx = 0; + + ftype__for_each_parameter(ftype, pos) { + if (idx == component_idx) + return &pos->tag; + ++idx; + } + + return NULL; +} + +static struct tag *function__parameter(const struct function *func, struct cu *cu, + int component_idx) +{ + struct tag *tag; + + if (component_idx < 0) + return NULL; + + tag = cu__type(cu, func->proto.tag.type); + if (tag == NULL) + return NULL; + + return ftype__parameter(tag__ftype(tag), component_idx); +} + static int process_decl_tag(struct cu *cu, const struct btf_type *tp) { + int component_idx = btf_decl_tag(tp)->component_idx; struct tag *tag = cu__type(cu, tp->type); struct attributes *tmp; - if (tag == NULL) + if (component_idx >= 0) { + struct tag *func_tag = cu__function(cu, tp->type); + + if (func_tag != NULL) { + tag = function__parameter(tag__function(func_tag), cu, + component_idx); + if (tag == NULL) { + fprintf(stderr, "WARNING: BTF_KIND_DECL_TAG for unknown parameter %d in BTF id %d\n", + component_idx, tp->type); + return 0; + } + } + } + + if (tag == NULL && component_idx < 0) tag = cu__function(cu, tp->type); if (tag == NULL) diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c index 757e499..ab1c381 100644 --- a/dwarves_fprintf.c +++ b/dwarves_fprintf.c @@ -1203,6 +1203,18 @@ const char *function__prototype(const struct function *func, bf, len); } +static size_t tag__attributes_fprintf(const struct tag *tag, FILE *fp) +{ + size_t printed = 0; + int i; + + if (tag->attributes) + for (i = 0; i < tag->attributes->cnt; ++i) + printed += fprintf(fp, "%s ", tag->attributes->values[i]); + + return printed; +} + size_t ftype__fprintf_parms(const struct ftype *ftype, const struct cu *cu, int indent, const struct conf_fprintf *conf, FILE *fp) @@ -1244,6 +1256,7 @@ size_t ftype__fprintf_parms(const struct ftype *ftype, if (n) return printed + n; if (ptype->tag == DW_TAG_subroutine_type) { + printed += tag__attributes_fprintf(&pos->tag, fp); printed += ftype__fprintf(tag__ftype(ptype), cu, name, 0, 1, 0, @@ -1252,12 +1265,14 @@ size_t ftype__fprintf_parms(const struct ftype *ftype, } } } else if (type->tag == DW_TAG_subroutine_type) { + printed += tag__attributes_fprintf(&pos->tag, fp); printed += ftype__fprintf(tag__ftype(type), cu, name, true, 0, 0, 0, conf, fp); continue; } stype = tag__name(type, cu, sbf, sizeof(sbf), conf); print_it: + printed += tag__attributes_fprintf(&pos->tag, fp); printed += fprintf(fp, "%s%s%s", stype, name ? " " : "", name ?: ""); } @@ -1409,11 +1424,8 @@ static size_t function__fprintf(const struct tag *tag, const struct cu *cu, struct ftype *ftype = func->btf ? tag__ftype(cu__type(cu, func->proto.tag.type)) : &func->proto; size_t printed = 0; bool inlined = !conf->strip_inline && function__declared_inline(func); - int i; - if (tag->attributes) - for (i = 0; i < tag->attributes->cnt; ++i) - printed += fprintf(fp, "%s ", tag->attributes->values[i]); + printed += tag__attributes_fprintf(tag, fp); if (func->virtuality == DW_VIRTUALITY_virtual || func->virtuality == DW_VIRTUALITY_pure_virtual) Now with that fix in place, we can add a decl tag parameter case to the pfunct test: diff --git a/tests/pfunct-btf-decl-tags.sh b/tests/pfunct-btf-decl-tags.sh index a46aa1f..520148b 100755 --- a/tests/pfunct-btf-decl-tags.sh +++ b/tests/pfunct-btf-decl-tags.sh @@ -41,6 +41,7 @@ src=$(cat <<EOF __tag(a) __tag(b) __tag(c) void foo(void) {} __tag(a) __tag(b) void bar(void) {} __tag(a) void buz(void) {} +void qux(int __tag(param_a) arg) {} EOF ) @@ -48,13 +49,17 @@ EOF # tags order is not guaranteed sort_tags=$(cat <<EOF { -match(\$0,/^(.*) (void .*)/,tags_and_proto); -tags = tags_and_proto[1]; -proto = tags_and_proto[2]; -split(tags, tags_arr ,/ /); -asort(tags_arr); -for (t in tags_arr) printf "%s ", tags_arr[t]; -print proto; +delete tags_arr; +if (match(\$0,/^(.*) (void .*)/,tags_and_proto)) { + tags = tags_and_proto[1]; + proto = tags_and_proto[2]; + split(tags, tags_arr ,/ /); + asort(tags_arr); + for (t in tags_arr) printf "%s ", tags_arr[t]; + print proto; +} else { + print \$0; +} } EOF ) @@ -63,6 +68,7 @@ expected=$(cat <<EOF a b c void foo(void); a b void bar(void); a void buz(void); +void qux(param_a int arg); EOF ) @@ -98,7 +104,8 @@ fi if [ "$use_clang" -eq 1 ]; then tmpobj=$(make_tmpobj) - echo "$src" | $CLANG --target=bpf -c -g -x c -o $tmpobj - + echo "$src" | $CLANG -c -g -x c -o $tmpobj - + pahole -J $tmpobj 2>/dev/null run_test "$CLANG" "$tmpobj" || failed=1 fi Running this we see: ./pfunct-btf-decl-tags.sh Check that pfunct can print btf_decl_tags read from BTF. Testing with gcc (version 17) pfunct output does not match expected (gcc (version 17)): --- /dev/fd/63 2026-06-07 10:42:19.711753983 +0100 +++ /dev/fd/62 2026-06-07 10:42:19.711753983 +0100 @@ -1,4 +1,4 @@ a b c void foo(void); a b void bar(void); a void buz(void); -void qux(param_a int arg); +void qux(int arg); Complete output: a b c void foo(void); a b void bar(void); a void buz(void); void qux(int arg); Testing with clang passed Test ./pfunct-btf-decl-tags.sh failed Test data is in /tmp/pfunct-btf-decl-tags.sh.hXPG6r The following change to dwarf_loader.c fixes this: diff --git a/dwarf_loader.c b/dwarf_loader.c index c492b56..7f5b44d 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -1910,6 +1910,8 @@ static struct tag *die__create_new_parameter(Dwarf_Die *die, if (param_idx >= 0) { if (add_child_llvm_annotations(die, param_idx, conf, &(tag__function(&ftype->tag)->annots))) return NULL; + if (add_gnu_annotation_chain(die, param_idx, conf, &(tag__function(&ftype->tag)->annots))) + return NULL; } } else { /* > Changes since v1 [1] > - NFC Reduce indentation with early exits (Alexei offlist) > > [3] https://lore.kernel.org/bpf/20260601183511.594100-2-vineet.gupta@linux.dev/ > [2] https://lore.kernel.org/bpf/20260528223616.2035618-2-vineet.gupta@linux.dev/ > [1] https://lore.kernel.org/bpf/20260526181818.4159927-2-vineet.gupta@linux.dev/ > --- > btf_encoder.c | 1 + > dutil.h | 11 +++++ > dwarf_loader.c | 105 +++++++++++++++++++++++++++++++++++++++------- > dwarves.h | 2 +- > dwarves_fprintf.c | 12 ++++-- > 5 files changed, 110 insertions(+), 21 deletions(-) > > diff --git a/btf_encoder.c b/btf_encoder.c > index 633bc6162ce0..d5af706d7638 100644 > --- a/btf_encoder.c > +++ b/btf_encoder.c > @@ -1831,6 +1831,7 @@ static int btf_encoder__encode_tag(struct btf_encoder *encoder, struct tag *tag, > name = namespace__name(tag__namespace(tag)); > return btf_encoder__add_ref_type(encoder, BTF_KIND_TYPEDEF, ref_type_id, name, false); > case DW_TAG_LLVM_annotation: > + case DW_TAG_GNU_annotation: > name = tag__btf_type_tag(tag)->value; > return btf_encoder__add_ref_type(encoder, BTF_KIND_TYPE_TAG, ref_type_id, name, false); > case DW_TAG_structure_type: > diff --git a/dutil.h b/dutil.h > index ff78aa6dfd10..be02c68cf4a7 100644 > --- a/dutil.h > +++ b/dutil.h > @@ -35,6 +35,17 @@ > #define DW_TAG_LLVM_annotation 0x6000 > #endif > > +#ifndef DW_TAG_GNU_annotation > +#define DW_TAG_GNU_annotation 0x6001 > +#endif > + > +#ifndef DW_AT_GNU_annotation > +#define DW_AT_GNU_annotation 0x2139 > +#endif > + > +#define tag__is_annotation(tag) \ > + ((tag) == DW_TAG_LLVM_annotation || (tag) == DW_TAG_GNU_annotation) > + > static inline __attribute__((const)) bool is_power_of_2(unsigned long n) > { > return (n != 0 && ((n & (n - 1)) == 0)); > diff --git a/dwarf_loader.c b/dwarf_loader.c > index 42a0f3f74ce3..14f71c9043aa 100644 > --- a/dwarf_loader.c > +++ b/dwarf_loader.c > @@ -908,7 +908,12 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b > return -ENOMEM; > } > > -static int add_llvm_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf, > +static bool die__tag_is_annotation(Dwarf_Die *die) > +{ > + return tag__is_annotation(dwarf_tag(die)); > +} > + > +static int add_tag_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf, > struct list_head *head) > { > struct llvm_annotation *annot; > @@ -943,16 +948,40 @@ static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx, > > die = &child; > do { > - if (dwarf_tag(die) == DW_TAG_LLVM_annotation) { > - ret = add_llvm_annotation(die, component_idx, conf, head); > - if (ret) > - return ret; > - } > + if (!die__tag_is_annotation(die)) > + continue; > + > + ret = add_tag_annotation(die, component_idx, conf, head); > + if (ret) > + return ret; > } while (dwarf_siblingof(die, die) == 0); > > return 0; > } > > +/* > + * Handle gcc style btf_decl_tag annotations for functions/struct/member tags. > + * Pointers are handled separately, inline in die__create_new_pointer_tag() > + */ > +static int add_gnu_annotation_chain(Dwarf_Die *die, int component_idx, > + struct conf_load *conf, struct list_head *head) > +{ > + Dwarf_Attribute attr; > + Dwarf_Die annot_die; > + > + while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL && > + dwarf_formref_die(&attr, &annot_die) != NULL && > + dwarf_tag(&annot_die) == DW_TAG_GNU_annotation) { > + int ret = add_tag_annotation(&annot_die, component_idx, conf, head); > + if (ret) > + return ret; > + > + die = &annot_die; > + } > + > + return 0; > +} > + > int class_member__dwarf_recode_bitfield(struct class_member *member, > struct cu *cu) > { > @@ -1596,6 +1625,8 @@ static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *di > return NULL; > > tag__init(&tag->tag, cu, die); > + /* Normalize DW_TAG_GNU_annotation to DW_TAG_LLVM_annotation internally */ > + tag->tag.tag = DW_TAG_LLVM_annotation; > tag->value = attr_string(die, DW_AT_const_value, conf); > return tag; > } > @@ -1637,19 +1668,21 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu, > { > struct btf_type_tag_ptr_type *tag = NULL; > Dwarf_Die *cdie, child; > + Dwarf_Attribute attr; > + Dwarf_Die annot_die; > const char *name; > > - /* If no child tags or skipping btf_type_tag encoding, just create a new tag > - * and return > - */ > - if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0 || > - conf->skip_encoding_btf_type_tag) > + /* If skipping btf_type_tag encoding, just create a new tag, return */ > + if (conf->skip_encoding_btf_type_tag) > return tag__new(die, cu); > > - /* Otherwise, check DW_TAG_LLVM_annotation child tags */ > + if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0) > + goto check_gnu_attr; > + > + /* Handle LLVM style annotation tags if present */ > cdie = &child; > do { > - if (dwarf_tag(cdie) != DW_TAG_LLVM_annotation) > + if (!die__tag_is_annotation(cdie)) > continue; > > /* Only check btf_type_tag annotations */ > @@ -1662,6 +1695,26 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu, > return NULL; > } while (dwarf_siblingof(cdie, cdie) == 0); > > +check_gnu_attr: > + if (tag != NULL) > + goto out; > + > + /* Handle GCC-style DW_AT_GNU_annotation attribute */ > + while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL && > + dwarf_formref_die(&attr, &annot_die) != NULL && > + dwarf_tag(&annot_die) == DW_TAG_GNU_annotation) { > + name = attr_string(&annot_die, DW_AT_name, conf); > + if (strcmp(name, "btf_type_tag") != 0) > + break; > + > + tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf); I think there may be an issue here. die__add_btf_type_tag() will prepend discovered tags to give us the BTF representation we want. However a proof-of-concept test shows that gcc tested with latest master branch as of commit 0e634b961123280c17c1c651b1d8b1567b9b523c (HEAD -> master, origin/trunk, origin/master, origin/HEAD) Author: Marc Poulhiès <poulhies@adacore.com> Date: Fri Mar 27 16:29:16 2026 +0100 ada: Fix bug when reading multibyte utf-8 character ...shows that the DWARF representation already has the BTF-desired order, so we wind up reversing it. Test below, but I _think_ something like this may be needed: diff --git a/dwarf_loader.c b/dwarf_loader.c index 14f71c9..c492b56 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -1633,7 +1633,8 @@ static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *di static struct btf_type_tag_ptr_type *die__add_btf_type_tag(struct btf_type_tag_ptr_type *tag, Dwarf_Die *die, Dwarf_Die *adie, - struct cu *cu, struct conf_load *conf) + struct cu *cu, struct conf_load *conf, + bool prepend) { struct btf_type_tag_type *annot; uint32_t id; @@ -1656,10 +1657,38 @@ static struct btf_type_tag_ptr_type *die__add_btf_type_tag(struct btf_type_tag_p cu__hash(cu, &annot->tag); /* - * Prepends: for annotations tag1 -> tag2 -> tag3, - * the tag->tags list ends up as tag3 -> tag2 -> tag1. + * Consider the following set of type tags: + * + * #define __tag(x) __attribute__((btf_type_tag(#x))) + * + * struct sample { + * int value; + * }; + * + * struct sample __tag(outer) __tag(inner) *global_ptr; + * + * The BTF chain should describe the pointer as: + * + * PTR -> TYPE_TAG "inner" -> TYPE_TAG "outer" -> STRUCT "sample" + * + * The tag nearest *global_ptr applies to the pointer’s immediate pointee + * type first. So inner wraps outer struct sample. + * + * For clang DWARF, child annotation DIEs are seen as "outer, inner" + * so to get the right final order we need to prepend. + * + * For GCC DWARF, the explicit DW_AT_GNU_annotation chain starts at the outermost + * BTF wrapper: + * + * pointer DIE -> inner annotation -> outer annotation -> struct sample + * + * so in this case we preserve that traversal order and append. */ - list_add(&annot->node, &tag->tags); + if (prepend) + list_add(&annot->node, &tag->tags); + else + list_add_tail(&annot->node, &tag->tags); + return tag; } @@ -1690,7 +1719,7 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu, if (strcmp(name, "btf_type_tag") != 0) continue; - tag = die__add_btf_type_tag(tag, die, cdie, cu, conf); + tag = die__add_btf_type_tag(tag, die, cdie, cu, conf, true); if (tag == NULL) return NULL; } while (dwarf_siblingof(cdie, cdie) == 0); @@ -1707,7 +1736,7 @@ check_gnu_attr: if (strcmp(name, "btf_type_tag") != 0) break; - tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf); + tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf, false); if (tag == NULL) : Here's a test (feel free to adapt it if needed) that fails until the above is applied: #!/bin/bash # SPDX-License-Identifier: GPL-2.0-only # Check that pahole preserves btf_type_tag order when emitting BTF from DWARF. source test_lib.sh outdir=$(make_tmpdir) # Comment this out to save test data. trap cleanup EXIT title_log "Check BTF type tag order." GCC=${GCC:-gcc} CLANG=${CLANG:-clang} PAHOLE=${PAHOLE:-pahole} BPFTOOL=${BPFTOOL:-bpftool} if ! command -v "$BPFTOOL" > /dev/null; then info_log "skip: bpftool not available" test_skip fi compiler_has_btf_type_tag() { local compiler=$1 if ! command -v "$compiler" > /dev/null; then return 1 fi "$compiler" -x c -E -P - <<'EOF' 2>/dev/null | grep -qx 1 #ifndef __has_attribute #define __has_attribute(x) 0 #endif #if __has_attribute(btf_type_tag) 1 #else 0 #endif EOF } use_gcc=0 if compiler_has_btf_type_tag "$GCC"; then use_gcc=1 fi use_clang=0 if compiler_has_btf_type_tag "$CLANG"; then use_clang=1 fi if [ "$use_gcc" -eq 0 ] && [ "$use_clang" -eq 0 ]; then error_log "Need gcc or clang with btf_type_tag support for test $0" test_fail fi src=$(cat <<EOF #define __tag(x) __attribute__((btf_type_tag(#x))) struct sample { int value; }; struct sample __tag(outer) __tag(inner) *global_ptr; EOF ) check_type_tag_order() { local btf=$1 local dump if ! dump=$("$BPFTOOL" btf dump file "$btf"); then return 1 fi printf '%s\n' "$dump" | awk ' function parse_id(line, m) { if (match(line, /^\[([0-9]+)\]/, m)) return m[1] return 0 } function parse_name(line, m) { if (match(line, /\047([^\047]*)\047/, m)) return m[1] return "" } function parse_type(line, m) { if (match(line, /type_id=([0-9]+)/, m)) return m[1] return 0 } function check_ptr(ptr, id, tags, seen) { id = type[ptr] while (id != 0 && !seen[id]) { seen[id] = 1 if (kind[id] == "TYPE_TAG") { tags = tags (tags == "" ? "" : " -> ") name[id] id = type[id] continue } if (kind[id] == "STRUCT" && name[id] == "sample") { if (tags == "inner -> outer") exit 0 candidates = candidates (candidates == "" ? "" : ", ") tags } return } } /^\[[0-9]+\]/ { id = parse_id($0) kind[id] = $2 name[id] = parse_name($0) type[id] = parse_type($0) if (kind[id] == "PTR") ptrs[++nr_ptrs] = id } END { for (i = 1; i <= nr_ptrs; i++) check_ptr(ptrs[i]) if (candidates != "") { print "type tag order mismatch; expected inner -> outer, found " candidates > "/dev/stderr" exit 1 } print "could not find tagged pointer to struct sample" > "/dev/stderr" exit 1 }' } run_test() { local compiler=$1 local tmpobj=$2 local btf=$3 info_log "Testing with $compiler" if ! echo "$src" | "$compiler" -g -c -x c -o "$tmpobj" - 2>/dev/null; then error_log "Could not compile type tag order test with $compiler" return 1 fi if ! "$PAHOLE" --btf_features=+type_tag --btf_encode_detached="$btf" "$tmpobj" 2>/dev/null; then error_log "Could not encode BTF for $tmpobj" return 1 fi if check_type_tag_order "$btf"; then info_log " passed" return 0 fi error_log "BTF type tag order does not match expected order ($compiler)" return 1 } failed=0 if [ "$use_gcc" -eq 1 ]; then tmpobj=$(make_tmpobj) btf=${tmpobj%.o}.btf run_test "$GCC" "$tmpobj" "$btf" || failed=1 fi if [ "$use_clang" -eq 1 ]; then tmpobj=$(make_tmpobj) btf=${tmpobj%.o}.btf run_test "$CLANG" "$tmpobj" "$btf" || failed=1 fi if [ "$failed" -eq 0 ]; then test_pass else test_fail fi ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation 2026-06-07 9:54 ` Alan Maguire @ 2026-06-17 20:08 ` Vineet Gupta 0 siblings, 0 replies; 16+ messages in thread From: Vineet Gupta @ 2026-06-17 20:08 UTC (permalink / raw) To: Alan Maguire, dwarves Cc: bpf, Andrii Nakryiko, acme, Emil Tsalapatis, jose.marchesi, David Faust On 6/7/26 2:54 AM, Alan Maguire wrote: > Thanks for the respin! Sorry for the delay took me a while to get set > up with latest gcc. No worries, and then I disappeared for a PTO. > Two things below, but in summary there are a few > things we need to line things up between clang and gcc representations > I think. To test these completely there are some existing btf_loader/pfunct > issues below that I've outlined; I can send patches for those fixes as > prerequisites, or feel free to roll into a respin, whatever you'd prefer. I don't mind the respin but will appreciate you sending the actual patches as you have much more context on this than me and will have easier time fielding any review requests. Also after reading the rest of email, these could even be follow ups to this series in spirit of incremental improvements (and the reason is not that I want to get the series out of the way ;-) but that these seem to be existing issues so might as well wait for something else to land - but your call really). > I don't want to introduce scope creep - and I may be missing something - > but it seems like the current changes don't cover decl tags for function > parameters. OK. > On this topic, there is an existing bug where decl tags > that have a component_idx reference are not rendered correctly by pfunct > which makes testing that properly impossible today. Parameter decl tags are > prepended to the function declaration rather than tied to the > parameter referenced by the component_idx, so we should fix that first. Hmm, it seems worthwhile putting this in a comment somewhere. > The root cause is btf_loader.c always attaches attributes to the function, > whereas it should use the component_idx to attach them to the parameter > tag where component_idx >= 0. > > The following changes are needed to fix that I think: > > diff --git a/btf_loader.c b/btf_loader.c > index b591219..9bb52c3 100644 > --- a/btf_loader.c > +++ b/btf_loader.c > @@ -477,12 +477,56 @@ static struct attributes *attributes__realloc(struct attributes *attributes, con > return result; > } > > +static struct tag *ftype__parameter(const struct ftype *ftype, int component_idx) > +{ > + struct parameter *pos; > + int idx = 0; > + > + ftype__for_each_parameter(ftype, pos) { > + if (idx == component_idx) > + return &pos->tag; > + ++idx; > + } > + > + return NULL; > +} > + > +static struct tag *function__parameter(const struct function *func, struct cu *cu, > + int component_idx) > +{ > + struct tag *tag; > + > + if (component_idx < 0) > + return NULL; > + > + tag = cu__type(cu, func->proto.tag.type); > + if (tag == NULL) > + return NULL; > + > + return ftype__parameter(tag__ftype(tag), component_idx); > +} > + > static int process_decl_tag(struct cu *cu, const struct btf_type *tp) > { > + int component_idx = btf_decl_tag(tp)->component_idx; > struct tag *tag = cu__type(cu, tp->type); > struct attributes *tmp; > > - if (tag == NULL) > + if (component_idx >= 0) { > + struct tag *func_tag = cu__function(cu, tp->type); > + > + if (func_tag != NULL) { > + tag = function__parameter(tag__function(func_tag), cu, > + component_idx); > + if (tag == NULL) { > + fprintf(stderr, "WARNING: BTF_KIND_DECL_TAG for unknown parameter %d in BTF id %d\n", > + component_idx, tp->type); > + return 0; > + } > + } > + } > + > + if (tag == NULL && component_idx < 0) > tag = cu__function(cu, tp->type); > > if (tag == NULL) > diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c > index 757e499..ab1c381 100644 > --- a/dwarves_fprintf.c > +++ b/dwarves_fprintf.c > @@ -1203,6 +1203,18 @@ const char *function__prototype(const struct function *func, > bf, len); > } > > +static size_t tag__attributes_fprintf(const struct tag *tag, FILE *fp) > +{ > + size_t printed = 0; > + int i; > + > + if (tag->attributes) > + for (i = 0; i < tag->attributes->cnt; ++i) > + printed += fprintf(fp, "%s ", tag->attributes->values[i]); > + > + return printed; > +} > + > size_t ftype__fprintf_parms(const struct ftype *ftype, > const struct cu *cu, int indent, > const struct conf_fprintf *conf, FILE *fp) > @@ -1244,6 +1256,7 @@ size_t ftype__fprintf_parms(const struct ftype *ftype, > if (n) > return printed + n; > if (ptype->tag == DW_TAG_subroutine_type) { > + printed += tag__attributes_fprintf(&pos->tag, fp); > printed += > ftype__fprintf(tag__ftype(ptype), > cu, name, 0, 1, 0, > @@ -1252,12 +1265,14 @@ size_t ftype__fprintf_parms(const struct ftype *ftype, > } > } > } else if (type->tag == DW_TAG_subroutine_type) { > + printed += tag__attributes_fprintf(&pos->tag, fp); > printed += ftype__fprintf(tag__ftype(type), cu, name, > true, 0, 0, 0, conf, fp); > continue; > } > stype = tag__name(type, cu, sbf, sizeof(sbf), conf); > print_it: > + printed += tag__attributes_fprintf(&pos->tag, fp); > printed += fprintf(fp, "%s%s%s", stype, name ? " " : "", > name ?: ""); > } > @@ -1409,11 +1424,8 @@ static size_t function__fprintf(const struct tag *tag, const struct cu *cu, > struct ftype *ftype = func->btf ? tag__ftype(cu__type(cu, func->proto.tag.type)) : &func->proto; > size_t printed = 0; > bool inlined = !conf->strip_inline && function__declared_inline(func); > - int i; > > - if (tag->attributes) > - for (i = 0; i < tag->attributes->cnt; ++i) > - printed += fprintf(fp, "%s ", tag->attributes->values[i]); > + printed += tag__attributes_fprintf(tag, fp); > > if (func->virtuality == DW_VIRTUALITY_virtual || > func->virtuality == DW_VIRTUALITY_pure_virtual) > > > Now with that fix in place, we can add a decl tag parameter case to the pfunct test: > > diff --git a/tests/pfunct-btf-decl-tags.sh b/tests/pfunct-btf-decl-tags.sh > index a46aa1f..520148b 100755 > --- a/tests/pfunct-btf-decl-tags.sh > +++ b/tests/pfunct-btf-decl-tags.sh > @@ -41,6 +41,7 @@ src=$(cat <<EOF > __tag(a) __tag(b) __tag(c) void foo(void) {} > __tag(a) __tag(b) void bar(void) {} > __tag(a) void buz(void) {} > +void qux(int __tag(param_a) arg) {} > > EOF > ) > @@ -48,13 +49,17 @@ EOF > # tags order is not guaranteed > sort_tags=$(cat <<EOF > { > -match(\$0,/^(.*) (void .*)/,tags_and_proto); > -tags = tags_and_proto[1]; > -proto = tags_and_proto[2]; > -split(tags, tags_arr ,/ /); > -asort(tags_arr); > -for (t in tags_arr) printf "%s ", tags_arr[t]; > -print proto; > +delete tags_arr; > +if (match(\$0,/^(.*) (void .*)/,tags_and_proto)) { > + tags = tags_and_proto[1]; > + proto = tags_and_proto[2]; > + split(tags, tags_arr ,/ /); > + asort(tags_arr); > + for (t in tags_arr) printf "%s ", tags_arr[t]; > + print proto; > +} else { > + print \$0; > +} > } > EOF > ) > @@ -63,6 +68,7 @@ expected=$(cat <<EOF > a b c void foo(void); > a b void bar(void); > a void buz(void); > +void qux(param_a int arg); > EOF > ) > > @@ -98,7 +104,8 @@ fi > > if [ "$use_clang" -eq 1 ]; then > tmpobj=$(make_tmpobj) > - echo "$src" | $CLANG --target=bpf -c -g -x c -o $tmpobj - > + echo "$src" | $CLANG -c -g -x c -o $tmpobj - > + pahole -J $tmpobj 2>/dev/null > run_test "$CLANG" "$tmpobj" || failed=1 > fi > > Running this we see: > > ./pfunct-btf-decl-tags.sh > Check that pfunct can print btf_decl_tags read from BTF. > Testing with gcc (version 17) > pfunct output does not match expected (gcc (version 17)): > --- /dev/fd/63 2026-06-07 10:42:19.711753983 +0100 +++ /dev/fd/62 2026-06-07 10:42:19.711753983 +0100 @@ -1,4 +1,4 @@ a b c void foo(void); a b void bar(void); a void buz(void); -void qux(param_a int arg); +void qux(int arg); > > Complete output: > a b c void foo(void); a b void bar(void); a void buz(void); void qux(int arg); > Testing with clang > passed > Test ./pfunct-btf-decl-tags.sh failed > Test data is in /tmp/pfunct-btf-decl-tags.sh.hXPG6r > > The following change to dwarf_loader.c fixes this: > > diff --git a/dwarf_loader.c b/dwarf_loader.c > index c492b56..7f5b44d 100644 > --- a/dwarf_loader.c > +++ b/dwarf_loader.c > @@ -1910,6 +1910,8 @@ static struct tag *die__create_new_parameter(Dwarf_Die *die, > if (param_idx >= 0) { > if (add_child_llvm_annotations(die, param_idx, conf, &(tag__function(&ftype->tag)->annots))) > return NULL; > + if (add_gnu_annotation_chain(die, param_idx, conf, &(tag__function(&ftype->tag)->annots))) > + return NULL; > } > } else { > /* If you agree, I'd prefer you send this as an independent pre/post fix. [snipped] >> +check_gnu_attr: >> + if (tag != NULL) >> + goto out; >> + >> + /* Handle GCC-style DW_AT_GNU_annotation attribute */ >> + while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL && >> + dwarf_formref_die(&attr, &annot_die) != NULL && >> + dwarf_tag(&annot_die) == DW_TAG_GNU_annotation) { >> + name = attr_string(&annot_die, DW_AT_name, conf); >> + if (strcmp(name, "btf_type_tag") != 0) >> + break; >> + >> + tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf); > > I think there may be an issue here. die__add_btf_type_tag() will prepend > discovered tags to give us the BTF representation we want. Great catch. I would never have figured this out on my own. > However a > proof-of-concept test shows that gcc tested with latest master branch > as of > > commit 0e634b961123280c17c1c651b1d8b1567b9b523c (HEAD -> master, origin/trunk, origin/master, origin/HEAD) > Author: Marc Poulhiès <poulhies@adacore.com> > Date: Fri Mar 27 16:29:16 2026 +0100 > > ada: Fix bug when reading multibyte utf-8 character > > > ...shows that the DWARF representation already has the BTF-desired > order, so we wind up reversing it. Test below, but I _think_ something > like this may be needed: > > diff --git a/dwarf_loader.c b/dwarf_loader.c > index 14f71c9..c492b56 100644 > --- a/dwarf_loader.c > +++ b/dwarf_loader.c > @@ -1633,7 +1633,8 @@ static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *di > > static struct btf_type_tag_ptr_type *die__add_btf_type_tag(struct btf_type_tag_ptr_type *tag, > Dwarf_Die *die, Dwarf_Die *adie, > - struct cu *cu, struct conf_load *conf) > + struct cu *cu, struct conf_load *conf, > + bool prepend) > { > struct btf_type_tag_type *annot; > uint32_t id; > @@ -1656,10 +1657,38 @@ static struct btf_type_tag_ptr_type *die__add_btf_type_tag(struct btf_type_tag_p > cu__hash(cu, &annot->tag); > > /* > - * Prepends: for annotations tag1 -> tag2 -> tag3, > - * the tag->tags list ends up as tag3 -> tag2 -> tag1. > + * Consider the following set of type tags: > + * > + * #define __tag(x) __attribute__((btf_type_tag(#x))) Bike shed, I'll just name it __type_tag. > + * > + * struct sample { > + * int value; > + * }; > + * > + * struct sample __tag(outer) __tag(inner) *global_ptr; > + * > + * The BTF chain should describe the pointer as: > + * > + * PTR -> TYPE_TAG "inner" -> TYPE_TAG "outer" -> STRUCT "sample" > + * > + * The tag nearest *global_ptr applies to the pointer’s immediate pointee > + * type first. So inner wraps outer struct sample. > + * > + * For clang DWARF, child annotation DIEs are seen as "outer, inner" > + * so to get the right final order we need to prepend. > + * > + * For GCC DWARF, the explicit DW_AT_GNU_annotation chain starts at the outermost > + * BTF wrapper: > + * > + * pointer DIE -> inner annotation -> outer annotation -> struct sample > + * > + * so in this case we preserve that traversal order and append. > */ > - list_add(&annot->node, &tag->tags); > + if (prepend) > + list_add(&annot->node, &tag->tags); > + else > + list_add_tail(&annot->node, &tag->tags); > + > return tag; > } > > @@ -1690,7 +1719,7 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu, > if (strcmp(name, "btf_type_tag") != 0) > continue; > > - tag = die__add_btf_type_tag(tag, die, cdie, cu, conf); > + tag = die__add_btf_type_tag(tag, die, cdie, cu, conf, true); > if (tag == NULL) > return NULL; > } while (dwarf_siblingof(cdie, cdie) == 0); > @@ -1707,7 +1736,7 @@ check_gnu_attr: > if (strcmp(name, "btf_type_tag") != 0) > break; > > - tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf); > + tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf, false); > if (tag == NULL) > : > > > Here's a test (feel free to adapt it if needed) that fails until the above is applied: > > #!/bin/bash > # SPDX-License-Identifier: GPL-2.0-only > > # Check that pahole preserves btf_type_tag order when emitting BTF from DWARF. > > source test_lib.sh > > outdir=$(make_tmpdir) > > # Comment this out to save test data. > trap cleanup EXIT > > title_log "Check BTF type tag order." > > GCC=${GCC:-gcc} > CLANG=${CLANG:-clang} > PAHOLE=${PAHOLE:-pahole} > BPFTOOL=${BPFTOOL:-bpftool} > > if ! command -v "$BPFTOOL" > /dev/null; then > info_log "skip: bpftool not available" > test_skip > fi > > compiler_has_btf_type_tag() > { > local compiler=$1 > > if ! command -v "$compiler" > /dev/null; then > return 1 > fi > > "$compiler" -x c -E -P - <<'EOF' 2>/dev/null | grep -qx 1 > #ifndef __has_attribute > #define __has_attribute(x) 0 > #endif > #if __has_attribute(btf_type_tag) > 1 > #else > 0 > #endif > EOF > } > > use_gcc=0 > if compiler_has_btf_type_tag "$GCC"; then > use_gcc=1 > fi > > use_clang=0 > if compiler_has_btf_type_tag "$CLANG"; then > use_clang=1 > fi > > if [ "$use_gcc" -eq 0 ] && [ "$use_clang" -eq 0 ]; then > error_log "Need gcc or clang with btf_type_tag support for test $0" > test_fail > fi > > src=$(cat <<EOF > #define __tag(x) __attribute__((btf_type_tag(#x))) > > struct sample { > int value; > }; > > struct sample __tag(outer) __tag(inner) *global_ptr; > > EOF > ) > > check_type_tag_order() > { > local btf=$1 > local dump > > if ! dump=$("$BPFTOOL" btf dump file "$btf"); then > return 1 > fi > > printf '%s\n' "$dump" | awk ' > function parse_id(line, m) { > if (match(line, /^\[([0-9]+)\]/, m)) > return m[1] > return 0 > } > function parse_name(line, m) { > if (match(line, /\047([^\047]*)\047/, m)) > return m[1] > return "" > } > function parse_type(line, m) { > if (match(line, /type_id=([0-9]+)/, m)) > return m[1] > return 0 > } > function check_ptr(ptr, id, tags, seen) { > id = type[ptr] > while (id != 0 && !seen[id]) { > seen[id] = 1 > if (kind[id] == "TYPE_TAG") { > tags = tags (tags == "" ? "" : " -> ") name[id] > id = type[id] > continue > } > if (kind[id] == "STRUCT" && name[id] == "sample") { > if (tags == "inner -> outer") > exit 0 > candidates = candidates (candidates == "" ? "" : ", ") tags > } > return > } > } > /^\[[0-9]+\]/ { > id = parse_id($0) > kind[id] = $2 > name[id] = parse_name($0) > type[id] = parse_type($0) > if (kind[id] == "PTR") > ptrs[++nr_ptrs] = id > } > END { > for (i = 1; i <= nr_ptrs; i++) > check_ptr(ptrs[i]) > if (candidates != "") { > print "type tag order mismatch; expected inner -> outer, found " candidates > "/dev/stderr" > exit 1 > } > print "could not find tagged pointer to struct sample" > "/dev/stderr" > exit 1 > }' > } > > run_test() > { > local compiler=$1 > local tmpobj=$2 > local btf=$3 > > info_log "Testing with $compiler" > > if ! echo "$src" | "$compiler" -g -c -x c -o "$tmpobj" - 2>/dev/null; then > error_log "Could not compile type tag order test with $compiler" > return 1 > fi > > if ! "$PAHOLE" --btf_features=+type_tag --btf_encode_detached="$btf" "$tmpobj" 2>/dev/null; then > error_log "Could not encode BTF for $tmpobj" > return 1 > fi > > if check_type_tag_order "$btf"; then > info_log " passed" > return 0 > fi > > error_log "BTF type tag order does not match expected order ($compiler)" > return 1 > } > > failed=0 > > if [ "$use_gcc" -eq 1 ]; then > tmpobj=$(make_tmpobj) > btf=${tmpobj%.o}.btf > run_test "$GCC" "$tmpobj" "$btf" || failed=1 > fi > > if [ "$use_clang" -eq 1 ]; then > tmpobj=$(make_tmpobj) > btf=${tmpobj%.o}.btf > run_test "$CLANG" "$tmpobj" "$btf" || failed=1 > fi > > if [ "$failed" -eq 0 ]; then > test_pass > else > test_fail > fi This is definitely directly related to the series and I'm happy to fold this in - any preference if this wants to be a separate patch or just fold into into the main change. Thx, -Vineet ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test 2026-06-02 19:55 [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta 2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta @ 2026-06-02 19:55 ` Vineet Gupta 2026-06-03 20:44 ` Emil Tsalapatis 2026-06-03 21:52 ` Yonghong Song 2026-06-03 20:18 ` [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Yonghong Song 2026-06-03 20:37 ` Emil Tsalapatis 3 siblings, 2 replies; 16+ messages in thread From: Vineet Gupta @ 2026-06-02 19:55 UTC (permalink / raw) To: dwarves Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis, jose.marchesi, David Faust, Vineet Gupta GCC 16+ supports btf_decl_tag via DW_TAG_GNU_annotation. Update the test to run with both GCC (>= 16) and clang when available, instead of requiring clang only. Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev> --- tests/pfunct-btf-decl-tags.sh | 72 +++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/tests/pfunct-btf-decl-tags.sh b/tests/pfunct-btf-decl-tags.sh index 35884b4e8687..a46aa1f3df55 100755 --- a/tests/pfunct-btf-decl-tags.sh +++ b/tests/pfunct-btf-decl-tags.sh @@ -6,24 +6,36 @@ source test_lib.sh outdir=$(make_tmpdir) -tmpobj=$(make_tmpobj) # Comment this out to save test data. trap cleanup EXIT title_log "Check that pfunct can print btf_decl_tags read from BTF." -# gcc now also supports decl tags as of gcc commit 43dcea48b8c, -# in upstream version 16. -# UPTODO: add a check here for that. +# gcc 16+ supports decl tags via DW_TAG_GNU_annotation (gcc commit ac7027f180b). +GCC=${GCC:-gcc} CLANG=${CLANG:-clang} -if ! command -v $CLANG > /dev/null; then - error_log "Need clang for test $0" + +use_gcc=0 +if command -v $GCC > /dev/null; then + gcc_ver=$($GCC -dumpversion 2>/dev/null | cut -d. -f1) + if [ "$gcc_ver" -ge 16 ] 2>/dev/null; then + use_gcc=1 + fi +fi + +use_clang=0 +if command -v $CLANG > /dev/null; then + use_clang=1 +fi + +if [ "$use_gcc" -eq 0 ] && [ "$use_clang" -eq 0 ]; then + error_log "Need gcc >= 16 or clang for test $0" test_fail fi -(cat <<EOF +src=$(cat <<EOF #define __tag(x) __attribute__((btf_decl_tag(#x))) __tag(a) __tag(b) __tag(c) void foo(void) {} @@ -31,7 +43,7 @@ __tag(a) __tag(b) void bar(void) {} __tag(a) void buz(void) {} EOF -) | $CLANG --target=bpf -c -g -x c -o $tmpobj - +) # tags order is not guaranteed sort_tags=$(cat <<EOF @@ -54,16 +66,44 @@ a void buz(void); EOF ) -out=$(pfunct -P -F btf $tmpobj | awk "$sort_tags" | sort) -d=$(diff -u <(echo "$expected") <(echo "$out")) +run_test() { + local compiler=$1 + local tmpobj=$2 + + info_log "Testing with $compiler" + out=$(pfunct -P -F btf $tmpobj | awk "$sort_tags" | sort) + d=$(diff -u <(echo "$expected") <(echo "$out")) + + if [[ "$d" == "" ]]; then + info_log " passed" + return 0 + else + error_log "pfunct output does not match expected ($compiler):" + info_log "$d" + info_log + info_log "Complete output:" + info_log "$out" + return 1 + fi +} + +failed=0 + +if [ "$use_gcc" -eq 1 ]; then + tmpobj=$(make_tmpobj) + echo "$src" | $GCC -c -g -x c -o $tmpobj - 2>/dev/null + pahole -J $tmpobj 2>/dev/null + run_test "$GCC (version $gcc_ver)" "$tmpobj" || failed=1 +fi + +if [ "$use_clang" -eq 1 ]; then + tmpobj=$(make_tmpobj) + echo "$src" | $CLANG --target=bpf -c -g -x c -o $tmpobj - + run_test "$CLANG" "$tmpobj" || failed=1 +fi -if [[ "$d" == "" ]]; then +if [ "$failed" -eq 0 ]; then test_pass else - error_log "pfunct output does not match expected:" - info_log "$d" - info_log - info_log "Complete output:" - info_log "$out" test_fail fi -- 2.54.0 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test 2026-06-02 19:55 ` [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta @ 2026-06-03 20:44 ` Emil Tsalapatis 2026-06-03 21:52 ` Yonghong Song 1 sibling, 0 replies; 16+ messages in thread From: Emil Tsalapatis @ 2026-06-03 20:44 UTC (permalink / raw) To: Vineet Gupta, dwarves Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis, jose.marchesi, David Faust On Tue Jun 2, 2026 at 3:55 PM EDT, Vineet Gupta wrote: > GCC 16+ supports btf_decl_tag via DW_TAG_GNU_annotation. Update the > test to run with both GCC (>= 16) and clang when available, instead > of requiring clang only. > > Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com> > --- > tests/pfunct-btf-decl-tags.sh | 72 +++++++++++++++++++++++++++-------- > 1 file changed, 56 insertions(+), 16 deletions(-) > > diff --git a/tests/pfunct-btf-decl-tags.sh b/tests/pfunct-btf-decl-tags.sh > index 35884b4e8687..a46aa1f3df55 100755 > --- a/tests/pfunct-btf-decl-tags.sh > +++ b/tests/pfunct-btf-decl-tags.sh > @@ -6,24 +6,36 @@ > source test_lib.sh > > outdir=$(make_tmpdir) > -tmpobj=$(make_tmpobj) > > # Comment this out to save test data. > trap cleanup EXIT > > title_log "Check that pfunct can print btf_decl_tags read from BTF." > > -# gcc now also supports decl tags as of gcc commit 43dcea48b8c, > -# in upstream version 16. > -# UPTODO: add a check here for that. > +# gcc 16+ supports decl tags via DW_TAG_GNU_annotation (gcc commit ac7027f180b). > > +GCC=${GCC:-gcc} > CLANG=${CLANG:-clang} > -if ! command -v $CLANG > /dev/null; then > - error_log "Need clang for test $0" > + > +use_gcc=0 > +if command -v $GCC > /dev/null; then > + gcc_ver=$($GCC -dumpversion 2>/dev/null | cut -d. -f1) > + if [ "$gcc_ver" -ge 16 ] 2>/dev/null; then > + use_gcc=1 > + fi > +fi > + > +use_clang=0 > +if command -v $CLANG > /dev/null; then > + use_clang=1 > +fi > + > +if [ "$use_gcc" -eq 0 ] && [ "$use_clang" -eq 0 ]; then > + error_log "Need gcc >= 16 or clang for test $0" > test_fail > fi > > -(cat <<EOF > +src=$(cat <<EOF > #define __tag(x) __attribute__((btf_decl_tag(#x))) > > __tag(a) __tag(b) __tag(c) void foo(void) {} > @@ -31,7 +43,7 @@ __tag(a) __tag(b) void bar(void) {} > __tag(a) void buz(void) {} > > EOF > -) | $CLANG --target=bpf -c -g -x c -o $tmpobj - > +) > > # tags order is not guaranteed > sort_tags=$(cat <<EOF > @@ -54,16 +66,44 @@ a void buz(void); > EOF > ) > > -out=$(pfunct -P -F btf $tmpobj | awk "$sort_tags" | sort) > -d=$(diff -u <(echo "$expected") <(echo "$out")) > +run_test() { > + local compiler=$1 > + local tmpobj=$2 > + > + info_log "Testing with $compiler" > + out=$(pfunct -P -F btf $tmpobj | awk "$sort_tags" | sort) > + d=$(diff -u <(echo "$expected") <(echo "$out")) > + > + if [[ "$d" == "" ]]; then > + info_log " passed" > + return 0 > + else > + error_log "pfunct output does not match expected ($compiler):" > + info_log "$d" > + info_log > + info_log "Complete output:" > + info_log "$out" > + return 1 > + fi > +} > + > +failed=0 > + > +if [ "$use_gcc" -eq 1 ]; then > + tmpobj=$(make_tmpobj) > + echo "$src" | $GCC -c -g -x c -o $tmpobj - 2>/dev/null > + pahole -J $tmpobj 2>/dev/null > + run_test "$GCC (version $gcc_ver)" "$tmpobj" || failed=1 > +fi > + > +if [ "$use_clang" -eq 1 ]; then > + tmpobj=$(make_tmpobj) > + echo "$src" | $CLANG --target=bpf -c -g -x c -o $tmpobj - > + run_test "$CLANG" "$tmpobj" || failed=1 > +fi > > -if [[ "$d" == "" ]]; then > +if [ "$failed" -eq 0 ]; then > test_pass > else > - error_log "pfunct output does not match expected:" > - info_log "$d" > - info_log > - info_log "Complete output:" > - info_log "$out" > test_fail > fi ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test 2026-06-02 19:55 ` [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta 2026-06-03 20:44 ` Emil Tsalapatis @ 2026-06-03 21:52 ` Yonghong Song 1 sibling, 0 replies; 16+ messages in thread From: Yonghong Song @ 2026-06-03 21:52 UTC (permalink / raw) To: Vineet Gupta, dwarves Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis, jose.marchesi, David Faust On 6/2/26 12:55 PM, Vineet Gupta wrote: > GCC 16+ supports btf_decl_tag via DW_TAG_GNU_annotation. Update the > test to run with both GCC (>= 16) and clang when available, instead > of requiring clang only. > > Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev> LGTM but missing 'pahole -J' for clang test. See below. Acked-by: Yonghong Song <yonghong.song@linux.dev> > --- > tests/pfunct-btf-decl-tags.sh | 72 +++++++++++++++++++++++++++-------- > 1 file changed, 56 insertions(+), 16 deletions(-) > > diff --git a/tests/pfunct-btf-decl-tags.sh b/tests/pfunct-btf-decl-tags.sh > index 35884b4e8687..a46aa1f3df55 100755 > --- a/tests/pfunct-btf-decl-tags.sh > +++ b/tests/pfunct-btf-decl-tags.sh > @@ -6,24 +6,36 @@ > source test_lib.sh > > outdir=$(make_tmpdir) > -tmpobj=$(make_tmpobj) > > # Comment this out to save test data. > trap cleanup EXIT > > title_log "Check that pfunct can print btf_decl_tags read from BTF." > > -# gcc now also supports decl tags as of gcc commit 43dcea48b8c, > -# in upstream version 16. > -# UPTODO: add a check here for that. > +# gcc 16+ supports decl tags via DW_TAG_GNU_annotation (gcc commit ac7027f180b). > > +GCC=${GCC:-gcc} > CLANG=${CLANG:-clang} > -if ! command -v $CLANG > /dev/null; then > - error_log "Need clang for test $0" > + > +use_gcc=0 > +if command -v $GCC > /dev/null; then > + gcc_ver=$($GCC -dumpversion 2>/dev/null | cut -d. -f1) > + if [ "$gcc_ver" -ge 16 ] 2>/dev/null; then > + use_gcc=1 > + fi > +fi > + > +use_clang=0 > +if command -v $CLANG > /dev/null; then > + use_clang=1 > +fi > + > +if [ "$use_gcc" -eq 0 ] && [ "$use_clang" -eq 0 ]; then > + error_log "Need gcc >= 16 or clang for test $0" > test_fail > fi > > -(cat <<EOF > +src=$(cat <<EOF > #define __tag(x) __attribute__((btf_decl_tag(#x))) > > __tag(a) __tag(b) __tag(c) void foo(void) {} > @@ -31,7 +43,7 @@ __tag(a) __tag(b) void bar(void) {} > __tag(a) void buz(void) {} > > EOF > -) | $CLANG --target=bpf -c -g -x c -o $tmpobj - > +) > > # tags order is not guaranteed > sort_tags=$(cat <<EOF > @@ -54,16 +66,44 @@ a void buz(void); > EOF > ) > > -out=$(pfunct -P -F btf $tmpobj | awk "$sort_tags" | sort) > -d=$(diff -u <(echo "$expected") <(echo "$out")) > +run_test() { > + local compiler=$1 > + local tmpobj=$2 > + > + info_log "Testing with $compiler" > + out=$(pfunct -P -F btf $tmpobj | awk "$sort_tags" | sort) > + d=$(diff -u <(echo "$expected") <(echo "$out")) > + > + if [[ "$d" == "" ]]; then > + info_log " passed" > + return 0 > + else > + error_log "pfunct output does not match expected ($compiler):" > + info_log "$d" > + info_log > + info_log "Complete output:" > + info_log "$out" > + return 1 > + fi > +} > + > +failed=0 > + > +if [ "$use_gcc" -eq 1 ]; then > + tmpobj=$(make_tmpobj) > + echo "$src" | $GCC -c -g -x c -o $tmpobj - 2>/dev/null > + pahole -J $tmpobj 2>/dev/null > + run_test "$GCC (version $gcc_ver)" "$tmpobj" || failed=1 > +fi > + > +if [ "$use_clang" -eq 1 ]; then > + tmpobj=$(make_tmpobj) > + echo "$src" | $CLANG --target=bpf -c -g -x c -o $tmpobj - missing 'pahole -J $tmpobj 2>/dev/null' here. > + run_test "$CLANG" "$tmpobj" || failed=1 > +fi > > -if [[ "$d" == "" ]]; then > +if [ "$failed" -eq 0 ]; then > test_pass > else > - error_log "pfunct output does not match expected:" > - info_log "$d" > - info_log > - info_log "Complete output:" > - info_log "$out" > test_fail > fi ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] 2026-06-02 19:55 [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta 2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta 2026-06-02 19:55 ` [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta @ 2026-06-03 20:18 ` Yonghong Song 2026-06-03 20:37 ` Emil Tsalapatis 3 siblings, 0 replies; 16+ messages in thread From: Yonghong Song @ 2026-06-03 20:18 UTC (permalink / raw) To: Vineet Gupta, dwarves Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis, jose.marchesi, David Faust On 6/2/26 12:55 PM, Vineet Gupta wrote: > NFC change preparing for DW_TAG_GNU_annotation support. > Extract the btf_type_tag annotation creation logic into helper > die__add_btf_type_tag(). > > Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev> Acked-by: Yonghong Song <yonghong.song@linux.dev> > --- > Changes since v3 [3] > - Fix comment to kernel style [Arnaldo] > > Changes since v2 [2] > - die__add_btf_type_tag() returns pointer not error code [Alan] > > Changes since v1 [1] > - NFC reinstate some original comments > > [3] https://lore.kernel.org/bpf/20260601183511.594100-1-vineet.gupta@linux.dev/ > [2] https://lore.kernel.org/bpf/20260528223616.2035618-1-vineet.gupta@linux.dev/ > [1] https://lore.kernel.org/bpf/20260526181818.4159927-1-vineet.gupta@linux.dev/ If there is a further revision, for a three patch series, I think it is worthwhile to have a cover letter so the above change log (and 2nd patch or more) can be all in cover letter. > --- > dwarf_loader.c | 58 +++++++++++++++++++++++++++++--------------------- > 1 file changed, 34 insertions(+), 24 deletions(-) > > diff --git a/dwarf_loader.c b/dwarf_loader.c > index 16fb7becffee..42a0f3f74ce3 100644 > --- a/dwarf_loader.c > +++ b/dwarf_loader.c > @@ -1600,14 +1600,44 @@ static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *di > return tag; > } > > [...] ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] 2026-06-02 19:55 [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta ` (2 preceding siblings ...) 2026-06-03 20:18 ` [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Yonghong Song @ 2026-06-03 20:37 ` Emil Tsalapatis 3 siblings, 0 replies; 16+ messages in thread From: Emil Tsalapatis @ 2026-06-03 20:37 UTC (permalink / raw) To: Vineet Gupta, dwarves Cc: bpf, Andrii Nakryiko, acme, Alan Maguire, Emil Tsalapatis, jose.marchesi, David Faust On Tue Jun 2, 2026 at 3:55 PM EDT, Vineet Gupta wrote: > NFC change preparing for DW_TAG_GNU_annotation support. > Extract the btf_type_tag annotation creation logic into helper > die__add_btf_type_tag(). > > Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com> > --- > Changes since v3 [3] > - Fix comment to kernel style [Arnaldo] > > Changes since v2 [2] > - die__add_btf_type_tag() returns pointer not error code [Alan] > > Changes since v1 [1] > - NFC reinstate some original comments > > [3] https://lore.kernel.org/bpf/20260601183511.594100-1-vineet.gupta@linux.dev/ > [2] https://lore.kernel.org/bpf/20260528223616.2035618-1-vineet.gupta@linux.dev/ > [1] https://lore.kernel.org/bpf/20260526181818.4159927-1-vineet.gupta@linux.dev/ > --- > dwarf_loader.c | 58 +++++++++++++++++++++++++++++--------------------- > 1 file changed, 34 insertions(+), 24 deletions(-) > > diff --git a/dwarf_loader.c b/dwarf_loader.c > index 16fb7becffee..42a0f3f74ce3 100644 > --- a/dwarf_loader.c > +++ b/dwarf_loader.c > @@ -1600,14 +1600,44 @@ static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *di > return tag; > } > > +static struct btf_type_tag_ptr_type *die__add_btf_type_tag(struct btf_type_tag_ptr_type *tag, > + Dwarf_Die *die, Dwarf_Die *adie, > + struct cu *cu, struct conf_load *conf) > +{ > + struct btf_type_tag_type *annot; > + uint32_t id; > + > + if (tag == NULL) { > + tag = die__create_new_btf_type_tag_ptr_type(die, cu); > + if (!tag) > + return NULL; > + } > + > + annot = die__create_new_btf_type_tag_type(adie, cu, conf); > + if (annot == NULL) > + return NULL; > + > + if (cu__table_add_tag(cu, &annot->tag, &id) < 0) > + return NULL; > + > + struct dwarf_tag *dtag = tag__dwarf(&annot->tag); > + dtag->small_id = id; > + cu__hash(cu, &annot->tag); > + > + /* > + * Prepends: for annotations tag1 -> tag2 -> tag3, > + * the tag->tags list ends up as tag3 -> tag2 -> tag1. > + */ > + list_add(&annot->node, &tag->tags); > + return tag; > +} > + > static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu, > struct conf_load *conf) > { > struct btf_type_tag_ptr_type *tag = NULL; > - struct btf_type_tag_type *annot; > Dwarf_Die *cdie, child; > const char *name; > - uint32_t id; > > /* If no child tags or skipping btf_type_tag encoding, just create a new tag > * and return > @@ -1627,29 +1657,9 @@ static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu, > if (strcmp(name, "btf_type_tag") != 0) > continue; > > - if (tag == NULL) { > - /* Create a btf_type_tag_ptr type. */ > - tag = die__create_new_btf_type_tag_ptr_type(die, cu); > - if (!tag) > - return NULL; > - } > - > - /* Create a btf_type_tag type for this annotation. */ > - annot = die__create_new_btf_type_tag_type(cdie, cu, conf); > - if (annot == NULL) > - return NULL; > - > - if (cu__table_add_tag(cu, &annot->tag, &id) < 0) > + tag = die__add_btf_type_tag(tag, die, cdie, cu, conf); > + if (tag == NULL) > return NULL; > - > - struct dwarf_tag *dtag = tag__dwarf(&annot->tag); > - dtag->small_id = id; > - cu__hash(cu, &annot->tag); > - > - /* For a list of DW_TAG_LLVM_annotation like tag1 -> tag2 -> tag3, > - * the tag->tags contains tag3 -> tag2 -> tag1. > - */ > - list_add(&annot->node, &tag->tags); > } while (dwarf_siblingof(cdie, cdie) == 0); > > return tag ? &tag->tag : tag__new(die, cu); ^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2026-06-17 20:08 UTC | newest] Thread overview: 16+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-02 19:55 [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Vineet Gupta 2026-06-02 19:55 ` [PAHOLE v4 2/3] dwarf_loader: Add support for DW_TAG_GNU_annotation Vineet Gupta 2026-06-03 20:08 ` Yonghong Song 2026-06-03 20:54 ` Vineet Gupta 2026-06-03 21:40 ` Yonghong Song 2026-06-17 18:18 ` Vineet Gupta 2026-06-03 20:42 ` Emil Tsalapatis 2026-06-03 21:41 ` Yonghong Song 2026-06-17 18:34 ` Vineet Gupta 2026-06-07 9:54 ` Alan Maguire 2026-06-17 20:08 ` Vineet Gupta 2026-06-02 19:55 ` [PAHOLE v4 3/3] tests: Support GCC in pfunct-btf-decl-tags test Vineet Gupta 2026-06-03 20:44 ` Emil Tsalapatis 2026-06-03 21:52 ` Yonghong Song 2026-06-03 20:18 ` [PAHOLE v4 1/3] dwarf_loader: Extract die__add_btf_type_tag() helper [NFC] Yonghong Song 2026-06-03 20:37 ` Emil Tsalapatis
This is 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.