Compilation generates DWARF at several stages, and often the later DWARF representations more accurately represent optimizations that have occurred during compilation. In particular, parameter representations can be spotted by their abstract origin references to the original parameter, but they often have more accurate location information. In most cases, the parameter locations will match calling conventions, and be registers for the first 6 parameters on x86_64, first 8 on ARM64 etc. If the parameter is not a register when it should be however, it is likely passed via the stack or the compiler has used a constant representation instead. This change adds a field to parameters and their associated ftype to note if a parameter has been optimized out. Having this information allows us to skip such functions, as their presence in CUs makes BTF encoding impossible. Signed-off-by: Alan Maguire <alan.maguire@xxxxxxxxxx> --- dwarf_loader.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- dwarves.h | 4 +++- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/dwarf_loader.c b/dwarf_loader.c index 5a74035..0220f1d 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -992,13 +992,67 @@ static struct class_member *class_member__new(Dwarf_Die *die, struct cu *cu, return member; } -static struct parameter *parameter__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) +/* How many function parameters are passed via registers? Used below in + * determining if an argument has been optimized out or if it is simply + * an argument > NR_REGISTER_PARAMS. Setting NR_REGISTER_PARAMS to 0 + * allows unsupported architectures to skip tagging optimized-out + * values. + */ +#if defined(__x86_64__) +#define NR_REGISTER_PARAMS 6 +#elif defined(__s390__) +#define NR_REGISTER_PARAMS 5 +#elif defined(__aarch64__) +#define NR_REGISTER_PARAMS 8 +#elif defined(__mips__) +#define NR_REGISTER_PARAMS 8 +#elif defined(__powerpc__) +#define NR_REGISTER_PARAMS 8 +#elif defined(__sparc__) +#define NR_REGISTER_PARAMS 6 +#elif defined(__riscv) && __riscv_xlen == 64 +#define NR_REGISTER_PARAMS 8 +#elif defined(__arc__) +#define NR_REGISTER_PARAMS 8 +#else +#define NR_REGISTER_PARAMS 0 +#endif + +static struct parameter *parameter__new(Dwarf_Die *die, struct cu *cu, + struct conf_load *conf, int param_idx) { struct parameter *parm = tag__alloc(cu, sizeof(*parm)); if (parm != NULL) { + struct location loc; + tag__init(&parm->tag, cu, die); parm->name = attr_string(die, DW_AT_name, conf); + + /* Parameters which use DW_AT_abstract_origin to point at + * the original parameter definition (with no name in the DIE) + * are the result of later DWARF generation during compilation + * so often better take into account if arguments were + * optimized out. + * + * By checking that locations for parameters that are expected + * to be passed as registers are actually passed as registers, + * we can spot optimized-out parameters. + */ + if (param_idx < NR_REGISTER_PARAMS && !parm->name && + attr_location(die, &loc.expr, &loc.exprlen) == 0 && + loc.exprlen != 0) { + Dwarf_Op *expr = loc.expr; + + switch (expr->atom) { + case DW_OP_reg1 ... DW_OP_reg31: + case DW_OP_breg0 ... DW_OP_breg31: + break; + default: + parm->optimized = true; + break; + } + } } return parm; @@ -1450,7 +1504,7 @@ static struct tag *die__create_new_parameter(Dwarf_Die *die, struct cu *cu, struct conf_load *conf, int param_idx) { - struct parameter *parm = parameter__new(die, cu, conf); + struct parameter *parm = parameter__new(die, cu, conf, param_idx); if (parm == NULL) return NULL; @@ -2209,6 +2263,10 @@ static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu) } pos->name = tag__parameter(dtype->tag)->name; pos->tag.type = dtype->tag->type; + if (pos->optimized) { + tag__parameter(dtype->tag)->optimized = pos->optimized; + type->optimized_parms = 1; + } continue; } @@ -2219,6 +2277,20 @@ static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu) } pos->tag.type = dtype->small_id; } + /* if parameters were optimized out, set flag for the ftype this + * function tag referred to via abstract origin. + */ + if (type->optimized_parms) { + struct dwarf_tag *dtype = type->tag.priv; + struct dwarf_tag *dftype; + + dftype = dwarf_cu__find_tag_by_ref(dcu, &dtype->abstract_origin); + if (dftype && dftype->tag) { + struct ftype *ftype = tag__ftype(dftype->tag); + + ftype->optimized_parms = 1; + } + } } static void lexblock__recode_dwarf_types(struct lexblock *tag, struct cu *cu) diff --git a/dwarves.h b/dwarves.h index 589588e..1ad1b3b 100644 --- a/dwarves.h +++ b/dwarves.h @@ -808,6 +808,7 @@ size_t lexblock__fprintf(const struct lexblock *lexblock, const struct cu *cu, struct parameter { struct tag tag; const char *name; + bool optimized; }; static inline struct parameter *tag__parameter(const struct tag *tag) @@ -827,7 +828,8 @@ struct ftype { struct tag tag; struct list_head parms; uint16_t nr_parms; - uint8_t unspec_parms; /* just one bit is needed */ + uint8_t unspec_parms:1; /* just one bit is needed */ + uint8_t optimized_parms:1; }; static inline struct ftype *tag__ftype(const struct tag *tag) -- 1.8.3.1