dwarf_loader: Support btf_type_tag attribute

LLVM patches ([1] for clang, [2] and [3] for BPF backend)
added support for btf_type_tag attributes. The following is
an example:

  [$ ~] cat t.c
  #define __tag1 __attribute__((btf_type_tag("tag1")))
  #define __tag2 __attribute__((btf_type_tag("tag2")))
  int __tag1 * __tag1 __tag2 *g __attribute__((section(".data..percpu")));
  [$ ~] clang -O2 -g -c t.c
  [$ ~] llvm-dwarfdump --debug-info t.o
  t.o:    file format elf64-x86-64
  ...
  0x0000001e:   DW_TAG_variable
                  DW_AT_name      ("g")
                  DW_AT_type      (0x00000033 "int **")
                  DW_AT_external  (true)
                  DW_AT_decl_file ("/home/yhs/t.c")
                  DW_AT_decl_line (3)
                  DW_AT_location  (DW_OP_addr 0x0)
  0x00000033:   DW_TAG_pointer_type
                  DW_AT_type      (0x0000004b "int *")
  0x00000038:     DW_TAG_LLVM_annotation
                    DW_AT_name    ("btf_type_tag")
                    DW_AT_const_value     ("tag1")
  0x00000041:     DW_TAG_LLVM_annotation
                    DW_AT_name    ("btf_type_tag")
                    DW_AT_const_value     ("tag2")
  0x0000004a:     NULL
  0x0000004b:   DW_TAG_pointer_type
                  DW_AT_type      (0x0000005a "int")
  0x00000050:     DW_TAG_LLVM_annotation
                    DW_AT_name    ("btf_type_tag")
                    DW_AT_const_value     ("tag1")
  0x00000059:     NULL
  0x0000005a:   DW_TAG_base_type
                  DW_AT_name      ("int")
                  DW_AT_encoding  (DW_ATE_signed)
                  DW_AT_byte_size (0x04)
  0x00000061:   NULL

From the above example, you can see that DW_TAG_pointer_type may contain
one or more DW_TAG_LLVM_annotation btf_type_tag tags.  If
DW_TAG_LLVM_annotation tags are present inside DW_TAG_pointer_type, for
BTF encoding, pahole will need to follow [3] to generate a type chain
like:

  var -> ptr -> tag2 -> tag1 -> ptr -> tag1 -> int

This patch implemented dwarf_loader support. If a pointer type contains
DW_TAG_LLVM_annotation tags, a new type btf_type_tag_ptr_type will be
created which will store the pointer tag itself and all
DW_TAG_LLVM_annotation tags.  During recoding stage, the type chain will
be formed properly based on the above example.

An option "--skip_encoding_btf_type_tag" is added to disable
this new functionality.

  [1] https://reviews.llvm.org/D111199
  [2] https://reviews.llvm.org/D113222
  [3] https://reviews.llvm.org/D113496

Signed-off-by: Yonghong Song <yhs@fb.com>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: bpf@vger.kernel.org
Cc: dwarves@vger.kernel.org
Cc: kernel-team@fb.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Yonghong Song 2021-11-22 20:56:28 -08:00 committed by Arnaldo Carvalho de Melo
parent a0cc68687f
commit b488c8d328
3 changed files with 173 additions and 4 deletions

View File

@ -1206,6 +1206,89 @@ static struct tag *die__create_new_tag(Dwarf_Die *die, struct cu *cu)
return tag;
}
static struct btf_type_tag_ptr_type *die__create_new_btf_type_tag_ptr_type(Dwarf_Die *die, struct cu *cu)
{
struct btf_type_tag_ptr_type *tag;
tag = tag__alloc_with_spec(cu, sizeof(struct btf_type_tag_ptr_type));
if (tag == NULL)
return NULL;
tag__init(&tag->tag, cu, die);
tag->tag.has_btf_type_tag = true;
INIT_LIST_HEAD(&tag->tags);
return tag;
}
static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *die, struct cu *cu,
struct conf_load *conf)
{
struct btf_type_tag_type *tag;
tag = tag__alloc_with_spec(cu, sizeof(struct btf_type_tag_type));
if (tag == NULL)
return NULL;
tag__init(&tag->tag, cu, die);
tag->value = attr_string(die, DW_AT_const_value, conf);
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
*/
if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0 ||
conf->skip_encoding_btf_type_tag)
return tag__new(die, cu);
/* Otherwise, check DW_TAG_LLVM_annotation child tags */
cdie = &child;
do {
if (dwarf_tag(cdie) != DW_TAG_LLVM_annotation)
continue;
/* Only check btf_type_tag annotations */
name = attr_string(cdie, DW_AT_name, conf);
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)
return NULL;
struct dwarf_tag *dtag = annot->tag.priv;
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);
}
static struct tag *die__create_new_ptr_to_member_type(Dwarf_Die *die,
struct cu *cu)
{
@ -1903,12 +1986,13 @@ static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu,
case DW_TAG_const_type:
case DW_TAG_imported_declaration:
case DW_TAG_imported_module:
case DW_TAG_pointer_type:
case DW_TAG_reference_type:
case DW_TAG_restrict_type:
case DW_TAG_unspecified_type:
case DW_TAG_volatile_type:
tag = die__create_new_tag(die, cu); break;
case DW_TAG_pointer_type:
tag = die__create_new_pointer_tag(die, cu, conf); break;
case DW_TAG_ptr_to_member_type:
tag = die__create_new_ptr_to_member_type(die, cu); break;
case DW_TAG_enumeration_type:
@ -2192,6 +2276,45 @@ static void lexblock__recode_dwarf_types(struct lexblock *tag, struct cu *cu)
}
}
static void dwarf_cu__recode_btf_type_tag_ptr(struct btf_type_tag_ptr_type *tag,
uint32_t pointee_type)
{
struct btf_type_tag_type *annot;
struct dwarf_tag *annot_dtag;
struct tag *prev_tag;
/* Given source like
* int tag1 tag2 tag3 *p;
* the tag->tags contains tag3 -> tag2 -> tag1, the final type chain looks like:
* pointer -> tag3 -> tag2 -> tag1 -> pointee
*
* Basically it means
* - '*' applies to "int tag1 tag2 tag3"
* - tag3 applies to "int tag1 tag2"
* - tag2 applies to "int tag1"
* - tag1 applies to "int"
*
* This also makes final source code (format c) easier as we can do
* emit for "tag3 -> tag2 -> tag1 -> int"
* emit '*'
*
* For 'tag3 -> tag2 -> tag1 -> int":
* emit for "tag2 -> tag1 -> int"
* emit tag3
*
* Eventually we can get the source code like
* int tag1 tag2 tag3 *p;
* and this matches the user/kernel code.
*/
prev_tag = &tag->tag;
list_for_each_entry(annot, &tag->tags, node) {
annot_dtag = annot->tag.priv;
prev_tag->type = annot_dtag->small_id;
prev_tag = &annot->tag;
}
prev_tag->type = pointee_type;
}
static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu)
{
struct dwarf_tag *dtag = tag->priv;
@ -2301,7 +2424,10 @@ static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu)
}
if (dtag->type.off == 0) {
tag->type = 0; /* void */
if (tag->tag != DW_TAG_pointer_type || !tag->has_btf_type_tag)
tag->type = 0; /* void */
else
dwarf_cu__recode_btf_type_tag_ptr(tag__btf_type_tag_ptr(tag), 0);
return 0;
}
@ -2313,7 +2439,11 @@ check_type:
return 0;
}
out:
tag->type = dtype->small_id;
if (tag->tag != DW_TAG_pointer_type || !tag->has_btf_type_tag)
tag->type = dtype->small_id;
else
dwarf_cu__recode_btf_type_tag_ptr(tag__btf_type_tag_ptr(tag), dtype->small_id);
return 0;
}

View File

@ -63,6 +63,7 @@ struct conf_load {
bool ptr_table_stats;
bool skip_encoding_btf_decl_tag;
bool skip_missing;
bool skip_encoding_btf_type_tag;
uint8_t hashtable_bits;
uint8_t max_hashtable_bits;
uint16_t kabi_prefix_len;
@ -413,6 +414,7 @@ struct tag {
uint16_t tag;
bool visited;
bool top_level;
bool has_btf_type_tag;
uint16_t recursivity_level;
void *priv;
};
@ -533,7 +535,8 @@ static inline int tag__is_tag_type(const struct tag *tag)
tag->tag == DW_TAG_restrict_type ||
tag->tag == DW_TAG_subroutine_type ||
tag->tag == DW_TAG_unspecified_type ||
tag->tag == DW_TAG_volatile_type;
tag->tag == DW_TAG_volatile_type ||
tag->tag == DW_TAG_LLVM_annotation;
}
static inline const char *tag__decl_file(const struct tag *tag,
@ -606,6 +609,34 @@ struct llvm_annotation {
struct list_head node;
};
/** struct btf_type_tag_type - representing a btf_type_tag annotation
*
* @tag - DW_TAG_LLVM_annotation tag
* @value - btf_type_tag value string
* @node - list_head node
*/
struct btf_type_tag_type {
struct tag tag;
const char *value;
struct list_head node;
};
/** The struct btf_type_tag_ptr_type - type containing both pointer type and
* its btf_type_tag annotations
*
* @tag - pointer type tag
* @tags - btf_type_tag annotations for the pointer type
*/
struct btf_type_tag_ptr_type {
struct tag tag;
struct list_head tags;
};
static inline struct btf_type_tag_ptr_type *tag__btf_type_tag_ptr(struct tag *tag)
{
return (struct btf_type_tag_ptr_type *)tag;
}
/** struct namespace - base class for enums, structs, unions, typedefs, etc
*
* @tags - class_member, enumerators, etc

View File

@ -1126,6 +1126,7 @@ ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
#define ARGP_devel_stats 330
#define ARGP_skip_encoding_btf_decl_tag 331
#define ARGP_skip_missing 332
#define ARGP_skip_encoding_btf_type_tag 333
static const struct argp_option pahole__options[] = {
{
@ -1506,6 +1507,11 @@ static const struct argp_option pahole__options[] = {
.key = ARGP_skip_missing,
.doc = "skip missing types passed to -C rather than stop",
},
{
.name = "skip_encoding_btf_type_tag",
.key = ARGP_skip_encoding_btf_type_tag,
.doc = "Do not encode TAGs in BTF."
},
{
.name = NULL,
}
@ -1658,6 +1664,8 @@ static error_t pahole__options_parser(int key, char *arg,
conf_load.skip_encoding_btf_decl_tag = true; break;
case ARGP_skip_missing:
conf_load.skip_missing = true; break;
case ARGP_skip_encoding_btf_type_tag:
conf_load.skip_encoding_btf_type_tag = true; break;
default:
return ARGP_ERR_UNKNOWN;
}