It is found on gcc 8.2 that global percpu variables generate the following dwarf entry in the cu where the variable is defined[1]. Take the global variable "bpf_prog_active" defined in kernel/bpf/syscall.c as an example. The debug info for syscall.c has two dwarf entries for "bpf_prog_active". > readelf -wi kernel/bpf/syscall.o 0x00013534: DW_TAG_variable DW_AT_name ("bpf_prog_active") DW_AT_decl_file ("/data/users/yhs/work/net-next/include/linux/bpf.h") DW_AT_decl_line (1074) DW_AT_decl_column (0x01) DW_AT_type (0x000000d6 "int") DW_AT_external (true) DW_AT_declaration (true) 0x00021a25: DW_TAG_variable DW_AT_specification (0x00013534 "bpf_prog_active") DW_AT_decl_file ("/data/users/yhs/work/net-next/kernel/bpf/syscall.c") DW_AT_decl_line (43) DW_AT_location (DW_OP_addr 0x0) Note that second DW_TAG_variable entry contains specification that points to the first entry. This causes problem for btf_encoder when encoding global variables. The tag generated for the second entry doesn't have the type and scope info. Therefore the BTF VARs encoded using this tag has incorrect type_id and scope. As fix, when creating variable, examine the dwarf entry. If it has a DW_AT_specification, store the referred struct variable in a 'spec' field. When encoding VARs, check this 'spec', if it's non-empty, follow the pointer to use the referred var. [1] https://www.mail-archive.com/netdev@xxxxxxxxxxxxxxx/msg348144.html Tested: Tested using gcc 4.9 and gcc 8.2. The types and scopes of global vars are now generated correctly. [21] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED [21102] VAR 'bpf_prog_active' type_id=21, linkage=global-alloc Signed-off-by: Hao Luo <haoluo@xxxxxxxxxx> --- btf_encoder.c | 16 +++++++++++++--- dwarf_loader.c | 25 ++++++++++++++++++++++++- dwarves.h | 1 + libbtf.c | 4 +++- 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/btf_encoder.c b/btf_encoder.c index 982f59d..29b2095 100644 --- a/btf_encoder.c +++ b/btf_encoder.c @@ -329,10 +329,10 @@ int cu__encode_btf(struct cu *cu, int verbose, bool force) struct hlist_head *head; var = tag__variable(pos); - if (var->declaration) + if (var->declaration && !var->spec) continue; /* percpu variables are allocated in global space */ - if (variable__scope(var) != VSCOPE_GLOBAL) + if (variable__scope(var) != VSCOPE_GLOBAL && !var->spec) continue; has_global_var = true; head = &hash_addr[hashaddr__fn(var->ip.addr)]; @@ -376,6 +376,8 @@ int cu__encode_btf(struct cu *cu, int verbose, bool force) var = hashaddr__find_variable(hash_addr, addr); if (var == NULL) continue; + if (var->spec) + var = var->spec; sym_name = elf_sym__name(&sym, btfe->symtab); if (!btf_name_valid(sym_name)) { @@ -387,7 +389,15 @@ int cu__encode_btf(struct cu *cu, int verbose, bool force) break; } name = strings__add(strings, sym_name); - type = var->ip.tag.type + type_id_off; + if (var->ip.tag.type == 0) { + dump_invalid_symbol("Found symbol of void type when encoding btf", + sym_name, cu->name, verbose, force); + if (force) + continue; + err = -1; + break; + } + type = type_id_off + var->ip.tag.type; size = elf_sym__size(&sym); if (!size) { dump_invalid_symbol("Found symbol of zero size when encoding btf", diff --git a/dwarf_loader.c b/dwarf_loader.c index b8d7b35..46f86cb 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -576,7 +576,15 @@ const char *variable__scope_str(const struct variable *var) static struct variable *variable__new(Dwarf_Die *die, struct cu *cu) { - struct variable *var = tag__alloc(cu, sizeof(*var)); + struct variable *var; + bool has_specification; + + has_specification = dwarf_hasattr(die, DW_AT_specification); + if (has_specification) { + var = tag__alloc_with_spec(cu, sizeof(*var)); + } else { + var = tag__alloc(cu, sizeof(*var)); + } if (var != NULL) { tag__init(&var->ip.tag, cu, die); @@ -589,6 +597,10 @@ static struct variable *variable__new(Dwarf_Die *die, struct cu *cu) var->ip.addr = 0; if (!var->declaration && cu->has_addr_info) var->scope = dwarf__location(die, &var->ip.addr, &var->location); + if (has_specification) { + dwarf_tag__set_spec(var->ip.tag.priv, + attr_type(die, DW_AT_specification)); + } } return var; @@ -2008,6 +2020,17 @@ static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu) if (dtype != NULL) goto out; goto find_type; + case DW_TAG_variable: { + struct variable *var = tag__variable(tag); + dwarf_off_ref specification = dwarf_tag__spec(dtag); + + if (specification.off) { + dtype = dwarf_cu__find_tag_by_ref(cu->priv, &specification); + if (dtype) + var->spec = tag__variable(dtype->tag); + } + } + } if (dtag->type.off == 0) { diff --git a/dwarves.h b/dwarves.h index 771a02c..bbd5478 100644 --- a/dwarves.h +++ b/dwarves.h @@ -679,6 +679,7 @@ struct variable { enum vscope scope; struct location location; struct hlist_node tool_hnode; + struct variable *spec; }; static inline struct variable *tag__variable(const struct tag *tag) diff --git a/libbtf.c b/libbtf.c index 7a01ded..ac06b79 100644 --- a/libbtf.c +++ b/libbtf.c @@ -304,6 +304,8 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = { [BTF_KIND_RESTRICT] = "RESTRICT", [BTF_KIND_FUNC] = "FUNC", [BTF_KIND_FUNC_PROTO] = "FUNC_PROTO", + [BTF_KIND_VAR] = "VAR", + [BTF_KIND_DATASEC] = "DATASEC", }; static const char *btf_elf__name_in_gobuf(const struct btf_elf *btfe, uint32_t offset) @@ -671,7 +673,7 @@ int32_t btf_elf__add_var_type(struct btf_elf *btfe, uint32_t type, uint32_t name return -1; } - btf_elf__log_type(btfe, &t.type, false, false, "type=%u name=%s", + btf_elf__log_type(btfe, &t.type, false, false, "type=%u name=%s\n", t.type.type, btf_elf__name_in_gobuf(btfe, t.type.name_off)); return btfe->type_index; -- 2.28.0.297.g1956fa8f8d-goog