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

 



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




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

  Powered by Linux