Re: pahole issues with Rust DWARF was: Re: [PATCH 1/1] pahole/Rust: Check that we're adding DW_TAG_member sorted by byte offset

From: Arnaldo Carvalho de Melo
Date: Mon Feb 13 2023 - 16:05:56 EST


Em Mon, Feb 13, 2023 at 01:45:02PM +0100, Miguel Ojeda escreveu:
> On Mon, Feb 13, 2023 at 1:09 PM Arnaldo Carvalho de Melo <arnaldo.melo@xxxxxxxxx> wrote:
> > The namespace.o seems to be ok:

> I saw the other message too -- this looks great, thanks a ton.

> > The core one needs work:

> If `core.o` works, then I think it is likely other things will work :)

> I can try to extract the cases for those into simpler `.o` files, if
> you would find simpler test cases useful (perhaps for the test suite
> etc.).

That would be great!

I tried starting this path with this, spitted out by ChatGPT (minus the
'pub' in from of main):

⬢[acme@toolbox pahole-rust-cases]$ cat template_type.rs
// Provided by ChatGPT

use std::ops::Add;

// Define a generic struct template with a single type parameter `T`.
struct Point<T> {
x: T,
y: T,
}

// Implement a method for the `Point` struct that adds two points together.
// The method uses the `Add` trait to ensure that the type `T` supports addition.
impl<T: Add<Output=T>> Point<T> {
fn add_points(self, other: Point<T>) -> Point<T> {
Point {
x: self.x + other.x,
y: self.y + other.y,
}
}
}

pub fn main() {
// Create two `Point` instances, using `i32` as the type parameter.
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 3, y: 4 };

// Add the two points together using the `add_points` method.
let p3 = p1.add_points(p2);

// Print the result.
println!("Point: ({}, {})", p3.x, p3.y);
}
⬢[acme@toolbox pahole-rust-cases]$

And then handle the DW_TAG_template_type_parameter for a
DW_TAG_subroutine:

<3><12d>: Abbrev Number: 5 (DW_TAG_structure_type)
<12e> DW_AT_name : (indirect string, offset: 0x299): ArgumentV1
<132> DW_AT_byte_size : 16
<133> DW_AT_alignment : 8
<4><134>: Abbrev Number: 6 (DW_TAG_member)
<135> DW_AT_name : (indirect string, offset: 0xd7): value
<139> DW_AT_type : <0x43a>
<13d> DW_AT_alignment : 8
<13e> DW_AT_data_member_location: 0
<4><13f>: Abbrev Number: 6 (DW_TAG_member)
<140> DW_AT_name : (indirect string, offset: 0x10e): formatter
<144> DW_AT_type : <0x447>
<148> DW_AT_alignment : 8
<149> DW_AT_data_member_location: 8
<4><14a>: Abbrev Number: 11 (DW_TAG_subprogram)
<14b> DW_AT_linkage_name: (indirect string, offset: 0x2a8): _ZN4core3fmt10ArgumentV13new17h167b8c43a2ee7614E
<14f> DW_AT_name : (indirect string, offset: 0x2d9): new<i32>
<153> DW_AT_decl_file : 2
<154> DW_AT_decl_line : 333
<156> DW_AT_type : <0x12d>
<15a> DW_AT_inline : 1 (inlined)
<5><15b>: Abbrev Number: 12 (DW_TAG_template_type_param)
<15c> DW_AT_type : <0x4e3>
<160> DW_AT_name : (indirect string, offset: 0x125): T
<5><164>: Abbrev Number: 13 (DW_TAG_variable)

I'll have to stop now, but was at the point of changing ftype__fprintf()
somehow to show that template type parameter, with the patch at the end
of this message I get this:

⬢[acme@toolbox pahole]$ pahole --show_private_classes ../pahole-rust-cases/template_type.o
die__process_class: tag not supported 0x33 (variant_part)!
die__process_class: tag not supported 0x2f (template_type_parameter)!
struct Argument {
usize position __attribute__((__aligned__(8))); /* 0 8 */
struct FormatSpec format __attribute__((__aligned__(8))); /* 8 48 */

/* XXX last struct has 7 bytes of padding */

/* size: 56, cachelines: 1, members: 2 */
/* paddings: 1, sum paddings: 7 */
/* forced alignments: 2 */
/* last cacheline: 56 bytes */
} __attribute__((__aligned__(8)));
struct FormatSpec {
struct Count precision __attribute__((__aligned__(8))); /* 0 16 */

/* XXX last struct has 16 bytes of padding */

struct Count width __attribute__((__aligned__(8))); /* 16 16 */

/* XXX last struct has 16 bytes of padding */

char fill __attribute__((__aligned__(4))); /* 32 4 */
u32 flags __attribute__((__aligned__(4))); /* 36 4 */
enum Alignment align __attribute__((__aligned__(1))); /* 40 1 */

/* size: 48, cachelines: 1, members: 5 */
/* padding: 7 */
/* paddings: 2, sum paddings: 32 */
/* forced alignments: 5 */
/* last cacheline: 48 bytes */
} __attribute__((__aligned__(8)));
struct Is {

/* XXX 8 bytes hole, try to pack */

usize __0 __attribute__((__aligned__(8))); /* 8 8 */

/* size: 16, cachelines: 1, members: 1 */
/* sum members: 8, holes: 1, sum holes: 8 */
/* forced alignments: 1, forced holes: 1, sum forced holes: 8 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct Param {

/* XXX 8 bytes hole, try to pack */

usize __0 __attribute__((__aligned__(8))); /* 8 8 */

/* size: 16, cachelines: 1, members: 1 */
/* sum members: 8, holes: 1, sum holes: 8 */
/* forced alignments: 1, forced holes: 1, sum forced holes: 8 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct Implied {

/* size: 16, cachelines: 1, members: 0 */
/* padding: 16 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct Count {

/* size: 16, cachelines: 1, members: 0 */
/* padding: 16 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct ArgumentV1 {
struct Opaque * value __attribute__((__aligned__(8))); /* 0 8 */
struct Result<(), core::fmt::Error> (*formatter)(struct Opaque *, struct Formatter *) __attribute__((__aligned__(8))); /* 8 8 */

/* size: 16, cachelines: 1, members: 2 */
/* forced alignments: 2 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct Opaque {

/* size: 0, cachelines: 0, members: 0 */
} __attribute__((__aligned__(1)));
struct Error {

/* size: 0, cachelines: 0, members: 0 */
} __attribute__((__aligned__(1)));
struct Formatter {
struct Option<usize> width __attribute__((__aligned__(8))); /* 0 16 */

/* XXX last struct has 16 bytes of padding */

struct Option<usize> precision __attribute__((__aligned__(8))); /* 16 16 */

/* XXX last struct has 16 bytes of padding */

struct &mut dyn core::fmt::Write buf __attribute__((__aligned__(8))); /* 32 16 */
u32 flags __attribute__((__aligned__(4))); /* 48 4 */
char fill __attribute__((__aligned__(4))); /* 52 4 */
enum Alignment align __attribute__((__aligned__(1))); /* 56 1 */

/* size: 64, cachelines: 1, members: 6 */
/* padding: 7 */
/* paddings: 2, sum paddings: 32 */
/* forced alignments: 6 */
} __attribute__((__aligned__(8)));
struct Arguments {
struct &[&str] pieces __attribute__((__aligned__(8))); /* 0 16 */
struct Option<&[core::fmt::rt::v1::Argument]> fmt __attribute__((__aligned__(8))); /* 16 16 */

/* XXX last struct has 16 bytes of padding */

struct &[core::fmt::ArgumentV1] args __attribute__((__aligned__(8))); /* 32 16 */

/* size: 48, cachelines: 1, members: 3 */
/* paddings: 1, sum paddings: 16 */
/* forced alignments: 3 */
/* last cacheline: 48 bytes */
} __attribute__((__aligned__(8)));
struct Ok {

/* XXX 1 byte hole, try to pack */

() __0 __attribute__((__aligned__(1))); /* 1 0 */

/* size: 1, cachelines: 1, members: 1 */
/* forced alignments: 1, forced holes: 1, sum forced holes: 1 */
/* last cacheline: 1 bytes */
} __attribute__((__aligned__(1)));
struct Err {

/* XXX 1 byte hole, try to pack */

struct Error __0 __attribute__((__aligned__(1))); /* 1 0 */

/* size: 1, cachelines: 1, members: 1 */
/* forced alignments: 1, forced holes: 1, sum forced holes: 1 */
/* last cacheline: 1 bytes */
} __attribute__((__aligned__(1)));
struct Result<(), core::fmt::Error> {

/* size: 1, cachelines: 0, members: 0 */
/* padding: 1 */
/* last cacheline: 1 bytes */
} __attribute__((__aligned__(1)));
struct None {

/* size: 16, cachelines: 1, members: 0 */
/* padding: 16 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct Some {

/* XXX 8 bytes hole, try to pack */

usize __0 __attribute__((__aligned__(8))); /* 8 8 */

/* size: 16, cachelines: 1, members: 1 */
/* sum members: 8, holes: 1, sum holes: 8 */
/* forced alignments: 1, forced holes: 1, sum forced holes: 8 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct Option<usize> {

/* size: 16, cachelines: 1, members: 0 */
/* padding: 16 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct Some {
struct &[core::fmt::rt::v1::Argument] __0 __attribute__((__aligned__(8))); /* 0 16 */

/* size: 16, cachelines: 1, members: 1 */
/* forced alignments: 1 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct Option<&[core::fmt::rt::v1::Argument]> {

/* size: 16, cachelines: 1, members: 0 */
/* padding: 16 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct &mut dyn core::fmt::Write {
struct dyn core::fmt::Write * pointer __attribute__((__aligned__(8))); /* 0 8 */
usize * vtable __attribute__((__aligned__(8))); /* 8 8 */

/* size: 16, cachelines: 1, members: 2 */
/* forced alignments: 2 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct dyn core::fmt::Write {

/* size: 0, cachelines: 0, members: 0 */
} __attribute__((__aligned__(1)));
struct &[&str] {
struct &str * data_ptr __attribute__((__aligned__(8))); /* 0 8 */
usize length __attribute__((__aligned__(8))); /* 8 8 */

/* size: 16, cachelines: 1, members: 2 */
/* forced alignments: 2 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct &str {
u8 * data_ptr __attribute__((__aligned__(8))); /* 0 8 */
usize length __attribute__((__aligned__(8))); /* 8 8 */

/* size: 16, cachelines: 1, members: 2 */
/* forced alignments: 2 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct &[core::fmt::rt::v1::Argument] {
struct Argument * data_ptr __attribute__((__aligned__(8))); /* 0 8 */
usize length __attribute__((__aligned__(8))); /* 8 8 */

/* size: 16, cachelines: 1, members: 2 */
/* forced alignments: 2 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct &[core::fmt::ArgumentV1] {
struct ArgumentV1 * data_ptr __attribute__((__aligned__(8))); /* 0 8 */
usize length __attribute__((__aligned__(8))); /* 8 8 */

/* size: 16, cachelines: 1, members: 2 */
/* forced alignments: 2 */
/* last cacheline: 16 bytes */
} __attribute__((__aligned__(8)));
struct Point<i32> {
i32 x __attribute__((__aligned__(4))); /* 0 4 */
i32 y __attribute__((__aligned__(4))); /* 4 4 */

/* size: 8, cachelines: 1, members: 2 */
/* forced alignments: 2 */
/* last cacheline: 8 bytes */
} __attribute__((__aligned__(4)));
⬢[acme@toolbox pahole]$

I'll cut version 1.25 with what is in the 'master' and 'next' now as
binutils 2.40 is out and emitting DW_TAG_unspecified_type, that isn't
supported by pahole 1.24 and also Alan's work on optimized functions,
etc.

- Arnaldo

diff --git a/dwarf_loader.c b/dwarf_loader.c
index a77598dc3affca88..b767bcaa9322c95a 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -1093,6 +1093,18 @@ static struct parameter *parameter__new(Dwarf_Die *die, struct cu *cu,
return parm;
}

+static struct template_type_parameter *template_type_parameter__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf)
+{
+ struct template_type_parameter *parm = tag__alloc(cu, sizeof(*parm));
+
+ if (parm != NULL) {
+ tag__init(&parm->tag, cu, die);
+ parm->name = attr_string(die, DW_AT_name, conf);
+ }
+
+ return parm;
+}
+
static struct inline_expansion *inline_expansion__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf)
{
struct inline_expansion *exp = tag__alloc(cu, sizeof(*exp));
@@ -1211,6 +1223,7 @@ static void ftype__init(struct ftype *ftype, Dwarf_Die *die, struct cu *cu)
#endif
tag__init(&ftype->tag, cu, die);
INIT_LIST_HEAD(&ftype->parms);
+ INIT_LIST_HEAD(&ftype->template_type_parms);
ftype->nr_parms = 0;
ftype->unspec_parms = 0;
}
@@ -1566,6 +1579,19 @@ static struct tag *die__create_new_parameter(Dwarf_Die *die,
return &parm->tag;
}

+static struct tag *die__create_new_template_type_parameter(Dwarf_Die *die, struct ftype *ftype,
+ struct cu *cu, struct conf_load *conf)
+{
+ struct template_type_parameter *parm = template_type_parameter__new(die, cu, conf);
+
+ if (parm == NULL)
+ return NULL;
+
+ ftype__add_template_type_parameter(ftype, parm);
+
+ return &parm->tag;
+}
+
static struct tag *die__create_new_label(Dwarf_Die *die,
struct lexblock *lexblock,
struct cu *cu, struct conf_load *conf)
@@ -1989,6 +2015,8 @@ static int die__process_function(Dwarf_Die *die, struct ftype *ftype,
case DW_TAG_GNU_template_template_param:
#endif
case DW_TAG_template_type_parameter:
+ tag = die__create_new_template_type_parameter(die, ftype, cu, conf);
+ break;
case DW_TAG_template_value_parameter:
/* FIXME: probably we'll have to attach this as a list of
* template parameters to use at class__fprintf time...
diff --git a/dwarves.c b/dwarves.c
index b43031c93c5cab58..6a8feccc2d45c60d 100644
--- a/dwarves.c
+++ b/dwarves.c
@@ -1391,6 +1391,12 @@ void ftype__add_parameter(struct ftype *ftype, struct parameter *parm)
list_add_tail(&parm->tag.node, &ftype->parms);
}

+void ftype__add_template_type_parameter(struct ftype *ftype, struct template_type_parameter *parm)
+{
+ ++ftype->nr_template_type_parms;
+ list_add_tail(&parm->tag.node, &ftype->template_type_parms);
+}
+
void lexblock__add_tag(struct lexblock *block, struct tag *tag)
{
list_add_tail(&tag->node, &block->tags);
diff --git a/dwarves.h b/dwarves.h
index 24a1909a60389dee..5c9952bfddaed301 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -825,13 +825,30 @@ static inline const char *parameter__name(const struct parameter *parm)
return parm->name;
}

+struct template_type_parameter {
+ struct tag tag;
+ const char *name;
+};
+
+static inline struct template_type_parameter *tag__template_type_parameter(const struct tag *tag)
+{
+ return (struct template_type_parameter *)tag;
+}
+
+static inline const char *template_type_parameter__name(const struct template_type_parameter *parm)
+{
+ return parm->name;
+}
+
/*
* tag.tag can be DW_TAG_subprogram_type or DW_TAG_subroutine_type.
*/
struct ftype {
struct tag tag;
struct list_head parms;
+ struct list_head template_type_parms;
uint16_t nr_parms;
+ uint16_t nr_template_type_parms;
uint8_t unspec_parms:1; /* just one bit is needed */
uint8_t optimized_parms:1;
uint8_t processed:1;
@@ -872,6 +889,8 @@ void ftype__delete(struct ftype *ftype);
list_for_each_entry_safe_reverse(pos, n, &(ftype)->parms, tag.node)

void ftype__add_parameter(struct ftype *ftype, struct parameter *parm);
+void ftype__add_template_type_parameter(struct ftype *ftype, struct template_type_parameter *parm);
+
size_t ftype__fprintf(const struct ftype *ftype, const struct cu *cu,
const char *name, const int inlined,
const int is_pointer, const int type_spacing, bool is_prototype,