Re: [PATCH v1] btf_encoder: Handle DW_TAG_variable that has DW_AT_specification

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Em Mon, Aug 24, 2020 at 05:45:23PM -0700, Hao Luo escreveu:
> 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)

Interesting, here I get, with binutils' readelf:

[root@quaco perf]# readelf -wi ../build/v5.8-rc5+/kernel/bpf/syscall.o | grep bpf_prog_active
    <f6a1>   DW_AT_name        : (indirect string, offset: 0xb70d): bpf_prog_active
[root@quaco perf]#

Just one, as:

[root@quaco perf]# readelf -wi ../build/v5.8-rc5+/kernel/bpf/syscall.o | grep bpf_prog_active -B1 -A8
 <1><f6a0>: Abbrev Number: 103 (DW_TAG_variable)
    <f6a1>   DW_AT_name        : (indirect string, offset: 0xb70d): bpf_prog_active
    <f6a5>   DW_AT_decl_file   : 11
    <f6a6>   DW_AT_decl_line   : 1008
    <f6a8>   DW_AT_decl_column : 1
    <f6a9>   DW_AT_type        : <0xcf>
    <f6ad>   DW_AT_external    : 1
    <f6ad>   DW_AT_declaration : 1
 <1><f6ad>: Abbrev Number: 103 (DW_TAG_variable)
    <f6ae>   DW_AT_name        : (indirect string, offset: 0x3a5d): bpf_stats_enabled_mutex
[root@quaco perf]#

I get what you have when I use elfutils' readelf:

[root@quaco perf]# eu-readelf -winfo ../build/v5.8-rc5+/kernel/bpf/syscall.o | grep bpf_prog_active
             name                 (strp) "bpf_prog_active"
              [ 0] addr .data..percpu+0 <bpf_prog_active>
[root@quaco perf]#

[root@quaco perf]# eu-readelf -winfo ../build/v5.8-rc5+/kernel/bpf/syscall.o | grep -B1 -A8 \"bpf_prog_active\"
 [  f6a0]    variable             abbrev: 103
             name                 (strp) "bpf_prog_active"
             decl_file            (data1) bpf.h (11)
             decl_line            (data2) 1008
             decl_column          (data1) 1
             type                 (ref4) [    cf]
             external             (flag_present) yes
             declaration          (flag_present) yes
 [  f6ad]    variable             abbrev: 103
             name                 (strp) "bpf_stats_enabled_mutex"
[root@quaco perf]#

And:

[root@quaco perf]# eu-readelf -winfo ../build/v5.8-rc5+/kernel/bpf/syscall.o | grep -B5 \<bpf_prog_active\>
 [ 1bdf5]    variable             abbrev: 212
             specification        (ref4) [  f6a0]
             decl_file            (data1) syscall.c (1)
             decl_line            (data1) 43
             location             (exprloc) 
              [ 0] addr .data..percpu+0 <bpf_prog_active>
[root@quaco perf]# 
 
> Note that second DW_TAG_variable entry contains specification that
> points to the first entry.

So you are not considering the first when encoding since it is just a
DW_AT_declaration, considers the second, as it should be, and then needs
to go see its DW_AT_specification, right?

Sounds correct, applying, will test further and then push out,

Thanks,

- Arnaldo

> 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
> 

-- 

- Arnaldo



[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux