392 lines
10 KiB
C
392 lines
10 KiB
C
/*
|
||
SPDX-License-Identifier: GPL-2.0-only
|
||
|
||
Copyright (C) 2006 Mandriva Conectiva S.A.
|
||
Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
|
||
Copyright (C) 2007 Red Hat Inc.
|
||
Copyright (C) 2007 Arnaldo Carvalho de Melo <acme@redhat.com>
|
||
*/
|
||
|
||
#include <string.h>
|
||
|
||
#include "list.h"
|
||
#include "dwarves_emit.h"
|
||
#include "dwarves.h"
|
||
|
||
void type_emissions__init(struct type_emissions *emissions)
|
||
{
|
||
INIT_LIST_HEAD(&emissions->definitions);
|
||
INIT_LIST_HEAD(&emissions->fwd_decls);
|
||
}
|
||
|
||
static void type_emissions__add_definition(struct type_emissions *emissions,
|
||
struct type *type)
|
||
{
|
||
type->definition_emitted = 1;
|
||
if (!list_empty(&type->node))
|
||
list_del(&type->node);
|
||
list_add_tail(&type->node, &emissions->definitions);
|
||
}
|
||
|
||
static void type_emissions__add_fwd_decl(struct type_emissions *emissions,
|
||
struct type *type)
|
||
{
|
||
type->fwd_decl_emitted = 1;
|
||
if (list_empty(&type->node))
|
||
list_add_tail(&type->node, &emissions->fwd_decls);
|
||
}
|
||
|
||
struct type *type_emissions__find_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 &&
|
||
strcmp(type__name(pos), name) == 0)
|
||
return pos;
|
||
|
||
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)
|
||
{
|
||
struct type *pos;
|
||
|
||
if (name == NULL)
|
||
return NULL;
|
||
|
||
list_for_each_entry(pos, &emissions->fwd_decls, node) {
|
||
const char *curr_name = type__name(pos);
|
||
|
||
if (curr_name && strcmp(curr_name, name) == 0)
|
||
return pos;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
static int enumeration__emit_definitions(struct tag *tag,
|
||
struct type_emissions *emissions,
|
||
const struct conf_fprintf *conf,
|
||
FILE *fp)
|
||
{
|
||
struct type *etype = tag__type(tag);
|
||
|
||
/* Have we already emitted this in this CU? */
|
||
if (etype->definition_emitted)
|
||
return 0;
|
||
|
||
/* Ok, lets look at the previous CUs: */
|
||
if (type_emissions__find_definition(emissions, DW_TAG_enumeration_type, type__name(etype)) != NULL) {
|
||
/*
|
||
* Yes, so lets mark it visited on this CU too,
|
||
* to speed up the lookup.
|
||
*/
|
||
etype->definition_emitted = 1;
|
||
return 0;
|
||
}
|
||
|
||
enumeration__fprintf(tag, conf, fp);
|
||
fputs(";\n", fp);
|
||
type_emissions__add_definition(emissions, etype);
|
||
return 1;
|
||
}
|
||
|
||
static int tag__emit_definitions(struct tag *tag, struct cu *cu,
|
||
struct type_emissions *emissions, FILE *fp);
|
||
|
||
static int typedef__emit_definitions(struct tag *tdef, struct cu *cu,
|
||
struct type_emissions *emissions, FILE *fp)
|
||
{
|
||
struct type *def = tag__type(tdef);
|
||
struct tag *type, *ptr_type;
|
||
|
||
/* Have we already emitted this in this CU? */
|
||
if (def->definition_emitted)
|
||
return 0;
|
||
|
||
/* Ok, lets look at the previous CUs: */
|
||
if (type_emissions__find_definition(emissions, DW_TAG_typedef, type__name(def)) != NULL) {
|
||
/*
|
||
* Yes, so lets mark it visited on this CU too,
|
||
* to speed up the lookup.
|
||
*/
|
||
def->definition_emitted = 1;
|
||
return 0;
|
||
}
|
||
|
||
type = cu__type(cu, tdef->type);
|
||
tag__assert_search_result(type);
|
||
|
||
switch (type->tag) {
|
||
case DW_TAG_array_type:
|
||
tag__emit_definitions(type, cu, emissions, fp);
|
||
break;
|
||
case DW_TAG_typedef:
|
||
typedef__emit_definitions(type, cu, emissions, fp);
|
||
break;
|
||
case DW_TAG_pointer_type:
|
||
ptr_type = cu__type(cu, type->type);
|
||
/* void ** can make ptr_type be NULL */
|
||
if (ptr_type == NULL)
|
||
break;
|
||
if (ptr_type->tag == DW_TAG_typedef) {
|
||
typedef__emit_definitions(ptr_type, cu, emissions, fp);
|
||
break;
|
||
} else if (ptr_type->tag != DW_TAG_subroutine_type)
|
||
break;
|
||
type = ptr_type;
|
||
/* Fall thru */
|
||
case DW_TAG_subroutine_type:
|
||
ftype__emit_definitions(tag__ftype(type), cu, emissions, fp);
|
||
break;
|
||
case DW_TAG_enumeration_type: {
|
||
struct type *ctype = tag__type(type);
|
||
struct conf_fprintf conf = {
|
||
.suffix = NULL,
|
||
};
|
||
|
||
if (type__name(ctype) == NULL) {
|
||
fputs("typedef ", fp);
|
||
conf.suffix = type__name(def);
|
||
enumeration__emit_definitions(type, emissions, &conf, fp);
|
||
goto out;
|
||
} else
|
||
enumeration__emit_definitions(type, emissions, &conf, fp);
|
||
}
|
||
break;
|
||
case DW_TAG_structure_type:
|
||
case DW_TAG_union_type: {
|
||
struct type *ctype = tag__type(type);
|
||
|
||
if (type__name(ctype) == NULL) {
|
||
type__emit_definitions(type__tag(ctype), cu, emissions, fp);
|
||
type__emit(type__tag(ctype), cu, "typedef", type__name(def), fp);
|
||
goto out;
|
||
} else if (type__emit_definitions(type, cu, emissions, fp))
|
||
type__emit(type, cu, NULL, NULL, fp);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Recheck if the typedef was emitted, as there are cases, like
|
||
* wait_queue_t in the Linux kernel, that is against struct
|
||
* __wait_queue, that has a wait_queue_func_t member, a function
|
||
* typedef that has as one of its parameters a... wait_queue_t, that
|
||
* will thus be emitted before the function typedef, making a no go to
|
||
* redefine the typedef after struct __wait_queue.
|
||
*/
|
||
if (!def->definition_emitted) {
|
||
typedef__fprintf(tdef, cu, NULL, fp);
|
||
fputs(";\n", fp);
|
||
}
|
||
out:
|
||
type_emissions__add_definition(emissions, def);
|
||
return 1;
|
||
}
|
||
|
||
static int type__emit_fwd_decl(struct type *ctype, struct type_emissions *emissions, FILE *fp)
|
||
{
|
||
/* Have we already emitted this in this CU? */
|
||
if (ctype->fwd_decl_emitted)
|
||
return 0;
|
||
|
||
const char *name = type__name(ctype);
|
||
if (name == NULL)
|
||
return 0;
|
||
|
||
/* Ok, lets look at the previous CUs: */
|
||
if (type_emissions__find_fwd_decl(emissions, name) != NULL) {
|
||
/*
|
||
* Yes, so lets mark it visited on this CU too,
|
||
* to speed up the lookup.
|
||
*/
|
||
ctype->fwd_decl_emitted = 1;
|
||
return 0;
|
||
}
|
||
|
||
fprintf(fp, "%s %s;\n",
|
||
tag__is_union(&ctype->namespace.tag) ? "union" : "struct",
|
||
type__name(ctype));
|
||
type_emissions__add_fwd_decl(emissions, ctype);
|
||
return 1;
|
||
}
|
||
|
||
static int tag__emit_definitions(struct tag *tag, struct cu *cu,
|
||
struct type_emissions *emissions, FILE *fp)
|
||
{
|
||
struct tag *type = cu__type(cu, tag->type);
|
||
int pointer = 0;
|
||
|
||
if (type == NULL)
|
||
return 0;
|
||
next_indirection:
|
||
switch (type->tag) {
|
||
case DW_TAG_pointer_type:
|
||
case DW_TAG_reference_type:
|
||
pointer = 1;
|
||
/* Fall thru */
|
||
case DW_TAG_array_type:
|
||
case DW_TAG_const_type:
|
||
case DW_TAG_volatile_type:
|
||
type = cu__type(cu, type->type);
|
||
if (type == NULL)
|
||
return 0;
|
||
goto next_indirection;
|
||
case DW_TAG_typedef:
|
||
return typedef__emit_definitions(type, cu, emissions, fp);
|
||
case DW_TAG_enumeration_type:
|
||
if (type__name(tag__type(type)) != NULL) {
|
||
struct conf_fprintf conf = {
|
||
.suffix = NULL,
|
||
};
|
||
return enumeration__emit_definitions(type, emissions, &conf, fp);
|
||
}
|
||
break;
|
||
case DW_TAG_structure_type:
|
||
case DW_TAG_union_type:
|
||
if (pointer) {
|
||
/*
|
||
* Struct defined inline, no name, need to have its
|
||
* members types emitted.
|
||
*/
|
||
if (type__name(tag__type(type)) == NULL)
|
||
type__emit_definitions(type, cu, emissions, fp);
|
||
|
||
return type__emit_fwd_decl(tag__type(type), emissions, fp);
|
||
}
|
||
if (type__emit_definitions(type, cu, emissions, fp))
|
||
type__emit(type, cu, NULL, NULL, fp);
|
||
return 1;
|
||
case DW_TAG_subroutine_type:
|
||
return ftype__emit_definitions(tag__ftype(type), cu,
|
||
emissions, fp);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int ftype__emit_definitions(struct ftype *ftype, struct cu *cu,
|
||
struct type_emissions *emissions, FILE *fp)
|
||
{
|
||
struct parameter *pos;
|
||
/* First check the function return type */
|
||
int printed = tag__emit_definitions(&ftype->tag, cu, emissions, fp);
|
||
|
||
/* Then its parameters */
|
||
list_for_each_entry(pos, &ftype->parms, tag.node)
|
||
if (tag__emit_definitions(&pos->tag, cu, emissions, fp))
|
||
printed = 1;
|
||
|
||
if (printed)
|
||
fputc('\n', fp);
|
||
return printed;
|
||
}
|
||
|
||
int type__emit_definitions(struct tag *tag, struct cu *cu,
|
||
struct type_emissions *emissions, FILE *fp)
|
||
{
|
||
struct type *ctype = tag__type(tag);
|
||
struct class_member *pos;
|
||
|
||
if (ctype->definition_emitted)
|
||
return 0;
|
||
|
||
/* Ok, lets look at the previous CUs: */
|
||
if (type_emissions__find_definition(emissions, tag->tag, type__name(ctype)) != NULL) {
|
||
ctype->definition_emitted = 1;
|
||
return 0;
|
||
}
|
||
|
||
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);
|
||
|
||
type__for_each_member(ctype, pos)
|
||
if (tag__emit_definitions(&pos->tag, cu, emissions, fp))
|
||
fputc('\n', fp);
|
||
|
||
return 1;
|
||
}
|
||
|
||
void type__emit(struct tag *tag, struct cu *cu,
|
||
const char *prefix, const char *suffix, FILE *fp)
|
||
{
|
||
struct type *ctype = tag__type(tag);
|
||
|
||
if (type__name(ctype) != NULL ||
|
||
suffix != NULL || prefix != NULL) {
|
||
struct conf_fprintf conf = {
|
||
.prefix = prefix,
|
||
.suffix = suffix,
|
||
.emit_stats = 1,
|
||
};
|
||
tag__fprintf(tag, cu, &conf, fp);
|
||
fputc('\n', fp);
|
||
}
|
||
}
|