emit: Notice type shadowing, i.e. multiple types with the same name (enum, struct, union, etc)

We can't have on the same compilation unit more than one of struct,
union, enum, class type with the same name, so when trying to create a
file with all types in a multi compilation unit binary debug
information we must disambiguate, do it by addint a __N prefix to the
type name.

This is the same strategy adopted by the bpftool when generating a
vmlinux file using:

  $ bpftool btf dump file vmlinux format c > vmlinux.h

Testing it:

  $ cat a.c
  #include "vmlinux.h"

  int main(void)
  {
  	struct saved_context bla;

  	bla.ds = 1;
  	return bla.ds + 1;
  }
  $

  $ rm -f a ; make a
  cc     a.c   -o a
  $

With an upcoming patch this will be possible using:

  $ pahole --compile > vmlinux.h
  $ rm -f a ; make a
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Arnaldo Carvalho de Melo 2022-02-08 15:46:55 -03:00
parent 0a82f74ce2
commit 4f332dbfd0
3 changed files with 66 additions and 0 deletions

View File

@ -345,6 +345,7 @@ void __type__init(struct type *type)
type->sizeof_member = NULL;
type->member_prefix = NULL;
type->member_prefix_len = 0;
type->suffix_disambiguation = 0;
}
struct class_member *
@ -1191,6 +1192,10 @@ void type__delete(struct type *type)
return;
type__delete_class_members(type);
if (type->suffix_disambiguation)
zfree(&type->namespace.name);
free(type);
}
@ -1211,6 +1216,9 @@ void enumeration__delete(struct type *type)
enumerator__delete(pos);
}
if (type->suffix_disambiguation)
zfree(&type->namespace.name);
free(type);
}

View File

@ -1015,6 +1015,8 @@ struct tag_cu_node {
* @nr_tags: number of tags
* @alignment: DW_AT_alignement, zero if not present, gcc emits since circa 7.3.1
* @natural_alignment: For inferring __packed__, normally the widest scalar in it, recursively
* @suffix_disambiguation: if we have both 'union foo' and 'struct foo' then we must disambiguate,
* useful to generate a vmlinux.h with all Linux types out of BTF data, for instance.
* @sizeof_member: Use this to find the size of the record
* @type_member: Use this to select a member from where to get an id on an enum to find a type
* to cast for, needs to be used with the upcoming type_enum.
@ -1038,6 +1040,7 @@ struct type {
uint16_t member_prefix_len;
uint16_t max_tag_name_len;
uint16_t natural_alignment;
uint8_t suffix_disambiguation;
uint8_t packed_attributes_inferred:1;
uint8_t declaration:1;
uint8_t definition_emitted:1;

View File

@ -53,6 +53,33 @@ struct type *type_emissions__find_definition(const struct type_emissions *emissi
return NULL;
}
static bool type__can_have_shadow_definition(struct type *type)
{
struct tag *tag = type__tag(type);
return tag__is_struct(tag) || tag__is_union(tag) || tag__is_enumeration(tag);
}
// Find if 'struct foo' is defined with a pre-existing 'enum foo', 'union foo', etc
struct type *type_emissions__find_shadow_definition(const struct type_emissions *emissions,
uint16_t tag, const char *name)
{
struct type *pos;
if (name == NULL)
return NULL;
list_for_each_entry(pos, &emissions->definitions, node) {
if (type__tag(pos)->tag != tag &&
type__name(pos) != NULL &&
type__can_have_shadow_definition(pos) &&
strcmp(type__name(pos), name) == 0)
return pos;
}
return NULL;
}
static struct type *type_emissions__find_fwd_decl(const struct type_emissions *emissions,
const char *name)
{
@ -307,6 +334,34 @@ int type__emit_definitions(struct tag *tag, struct cu *cu,
if (tag__is_typedef(tag))
return typedef__emit_definitions(tag, cu, emissions, fp);
/*
* vmlinux.h:120298:8: error: irte defined as wrong kind of tag
*
* If we have a 'struct foo' and we then find a 'union foo', which happens
* twice in the Linux kernel, for instance, then we need to disambiguate by
* adding a suffix to the second type with the same name.
*
* That is the strategy used in:
*
* btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
*/
if (type__can_have_shadow_definition(ctype)) {
if (type_emissions__find_shadow_definition(emissions, tag->tag, type__name(ctype))) {
ctype->suffix_disambiguation = 1;
char *disambiguated_name;
if (asprintf(&disambiguated_name, "%s__%u", type__name(ctype), ctype->suffix_disambiguation) == -1) {
fprintf(stderr, "emit: Not enough memory to allocate disambiguated type name for '%s'\n",
type__name(ctype));
} else {
// Will be deleted in type__delete() on noticing ctype->suffix_disambiguation != 0
tag__namespace(tag)->name = disambiguated_name;
}
}
}
type_emissions__add_definition(emissions, ctype);
type__check_structs_at_unnatural_alignments(ctype, cu);