btf: Allow multiple cu's in dwarf->btf conversion

Currently, the pahole dwarf->btf conversion only supports one
compilation unit. This is not ideal since we would like using pahole to
generate BTF for vmlinux which has a lot of compilation units.

This patch added support to process multiple compilation units per ELF
file. Multiple ELF files are also supported properly.

The following is a demonstration example:
  -bash-4.4$ cat t1.c
  struct t1 {
    int a1;
  } g1;
  int main(void) { return 0; }
  -bash-4.4$ cat t2.c
  struct t2 {
    char a2;
  } g2;
  int main() { return 0; }
  -bash-4.4$ cat t3.c
  struct t3 {
    unsigned char a1:4;
  } g1;
  int main(void) { return 0; }
  -bash-4.4$ cat t4.c
  struct t4 {
    volatile char a4;
  } g2;
  int main() { return 0; }
  -bash-4.4$ gcc -O2 -o t1 -g t1.c t2.c
  -bash-4.4$ gcc -O2 -o t3 -g t3.c t4.c

Note that both the binary "t1" and "t3" have two compilation units in
their respective dwarf debug_info sections. The following is the pahole
verbose output for BTF conversion for these two binaries.

  -bash-4.4$ pahole -JV t1 t3
  File t1:
  [1] STRUCT t1 size=4 vlen=1
        a1 type_id=2 bits_offset=0
  [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
  [3] STRUCT t2 size=1 vlen=1
        a2 type_id=4 bits_offset=0
  [4] INT char size=1 bit_offset=0 nr_bits=8 encoding=(none)
  [5] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED

  File t3:
  [1] STRUCT t3 size=1 vlen=1
        a1 type_id=3 bits_offset=0
  [2] INT unsigned char size=1 bit_offset=0 nr_bits=8 encoding=(none)
  [3] INT unsigned char size=1 bit_offset=0 nr_bits=4 encoding=(none)
  [4] INT (anon) size=4 bit_offset=0 nr_bits=32 encoding=(none)
  [5] STRUCT t4 size=1 vlen=1
        a4 type_id=6 bits_offset=0
  [6] VOLATILE (anon) type_id=7
  [7] INT char size=1 bit_offset=0 nr_bits=8 encoding=(none)
  [8] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Andrii Nakryiko 2018-12-18 14:09:39 -08:00 committed by Arnaldo Carvalho de Melo
parent d843945ba5
commit 65bd17abc7
4 changed files with 86 additions and 40 deletions

View File

@ -8,19 +8,22 @@
#include <inttypes.h>
static int tag__check_id_drift(const struct tag *tag,
uint32_t core_id, uint32_t btf_type_id)
uint32_t core_id, uint32_t btf_type_id,
uint32_t type_id_off)
{
if (btf_type_id != core_id) {
fprintf(stderr, "%s: %s id drift, core_id: %u, btf_type_id: %u\n",
if (btf_type_id != (core_id + type_id_off)) {
fprintf(stderr,
"%s: %s id drift, core_id: %u, btf_type_id: %u, type_id_off: %u\n",
__func__, dwarf_tag_name(tag->tag),
core_id, btf_type_id);
core_id, btf_type_id, type_id_off);
return -1;
}
return 0;
}
static int32_t structure_type__encode(struct btf *btf, struct tag *tag)
static int32_t structure_type__encode(struct btf *btf, struct tag *tag,
uint32_t type_id_off)
{
struct type *type = tag__type(tag);
struct class_member *pos;
@ -59,7 +62,8 @@ static int32_t structure_type__encode(struct btf *btf, struct tag *tag)
pos->bitfield_offset -
pos->bitfield_size;
if (btf__add_member(btf, pos->name, pos->tag.type,
if (btf__add_member(btf, pos->name,
type_id_off + pos->tag.type,
bit_offset))
return -1;
}
@ -107,21 +111,24 @@ static int32_t enumeration_type__encode(struct btf *btf, struct tag *tag)
}
static int tag__encode_btf(struct tag *tag, uint32_t core_id, struct btf *btf,
uint32_t array_index_id)
uint32_t array_index_id, uint32_t type_id_off)
{
/* single out type 0 as it represents special type "void" */
uint32_t ref_type_id = tag->type == 0 ? 0 : type_id_off + tag->type;
switch (tag->tag) {
case DW_TAG_base_type:
return btf__add_base_type(btf, tag__base_type(tag));
case DW_TAG_const_type:
return btf__add_ref_type(btf, BTF_KIND_CONST, tag->type, 0);
return btf__add_ref_type(btf, BTF_KIND_CONST, ref_type_id, 0);
case DW_TAG_pointer_type:
return btf__add_ref_type(btf, BTF_KIND_PTR, tag->type, 0);
return btf__add_ref_type(btf, BTF_KIND_PTR, ref_type_id, 0);
case DW_TAG_restrict_type:
return btf__add_ref_type(btf, BTF_KIND_RESTRICT, tag->type, 0);
return btf__add_ref_type(btf, BTF_KIND_RESTRICT, ref_type_id, 0);
case DW_TAG_volatile_type:
return btf__add_ref_type(btf, BTF_KIND_VOLATILE, tag->type, 0);
return btf__add_ref_type(btf, BTF_KIND_VOLATILE, ref_type_id, 0);
case DW_TAG_typedef:
return btf__add_ref_type(btf, BTF_KIND_TYPEDEF, tag->type,
return btf__add_ref_type(btf, BTF_KIND_TYPEDEF, ref_type_id,
tag__namespace(tag)->name);
case DW_TAG_structure_type:
case DW_TAG_union_type:
@ -130,9 +137,9 @@ static int tag__encode_btf(struct tag *tag, uint32_t core_id, struct btf *btf,
return btf__add_ref_type(btf, BTF_KIND_FWD, 0,
tag__namespace(tag)->name);
else
return structure_type__encode(btf, tag);
return structure_type__encode(btf, tag, type_id_off);
case DW_TAG_array_type:
return btf__add_array(btf, tag->type, array_index_id,
return btf__add_array(btf, ref_type_id, array_index_id,
/*TODO: Encode one dimension
* at a time.
*/
@ -157,40 +164,72 @@ static int tag__encode_btf(struct tag *tag, uint32_t core_id, struct btf *btf,
*/
extern struct strings *strings;
int cu__encode_btf(struct cu *cu, int verbose)
static struct btf *btf;
static uint32_t array_index_id;
int btf_encoder__encode()
{
struct btf *btf = btf__new(cu->filename, cu->elf);
struct tag *pos;
uint32_t core_id, array_index_id;
uint16_t id;
int err;
err = btf__encode(btf, 0);
btf__free(btf);
btf = NULL;
return err;
}
int cu__encode_btf(struct cu *cu, int verbose)
{
bool add_index_type = false;
uint32_t type_id_off;
uint32_t core_id;
struct tag *pos;
int err = 0;
if (btf && strcmp(btf->filename, cu->filename)) {
err = btf_encoder__encode();
if (err)
goto out;
/* Finished one file, add one empty line */
if (verbose)
printf("\n");
}
if (!btf) {
btf = btf__new(cu->filename, cu->elf);
if (!btf)
return -1;
btf__set_strings(btf, &strings->gb);
/* cu__find_base_type_by_name() takes "uint16_t *id" */
uint16_t id;
if (!cu__find_base_type_by_name(cu, "int", &id)) {
add_index_type = true;
id = cu->types_table.nr_entries;
}
array_index_id = id;
if (verbose)
printf("File %s:\n", btf->filename);
}
btf_verbose = verbose;
if (btf == NULL)
return -1;
btf__set_strings(btf, &strings->gb);
/* cu__find_base_type_by_name() takes "uint16_t *id" */
if (!cu__find_base_type_by_name(cu, "int", &id))
id = cu->types_table.nr_entries;
array_index_id = id;
type_id_off = btf->type_index;
cu__for_each_type(cu, core_id, pos) {
int32_t btf_type_id = tag__encode_btf(pos, core_id, btf,
array_index_id);
array_index_id,
type_id_off);
if (btf_type_id < 0 ||
tag__check_id_drift(pos, core_id, btf_type_id)) {
tag__check_id_drift(pos, core_id, btf_type_id, type_id_off)) {
err = -1;
goto out;
}
id = btf_type_id;
}
if (array_index_id == cu->types_table.nr_entries) {
if (add_index_type) {
struct base_type bt = {};
bt.name = 0;
@ -198,11 +237,8 @@ int cu__encode_btf(struct cu *cu, int verbose)
btf__add_base_type(btf, &bt);
}
err = btf__encode(btf, 0);
out:
btf__free(btf);
if (err)
fprintf(stderr, "Failed to encode BTF\n");
btf__free(btf);
return err;
}

View File

@ -3,6 +3,8 @@
struct cu;
int btf_encoder__encode();
int cu__encode_btf(struct cu *cu, int verbose);
#endif /* _BTF_ENCODER_H_ */

View File

@ -509,7 +509,7 @@ int btf__encode(struct btf *btf, uint8_t flags)
hdr = btf->hdr;
hdr->magic = BTF_MAGIC;
hdr->version = 1;
hdr->flags = 0;
hdr->flags = flags;
hdr->hdr_len = sizeof(*hdr);
hdr->type_off = 0;

View File

@ -1115,7 +1115,7 @@ static enum load_steal_kind pahole_stealer(struct cu *cu,
if (btf_encode) {
cu__encode_btf(cu, global_verbose);
goto dump_and_stop;
return LSK__KEEPIT;
}
if (ctf_encode) {
@ -1265,6 +1265,14 @@ int main(int argc, char *argv[])
goto out_cus_delete;
}
if (btf_encode) {
err = btf_encoder__encode();
if (err) {
fputs("Failed to encode BTF\n", stderr);
goto out_cus_delete;
}
}
if (stats_formatter != NULL)
print_stats();
rc = EXIT_SUCCESS;