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