dwarves/dwarves_emit.c

337 lines
8.5 KiB
C
Raw Normal View History

/*
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 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:
2019-04-16 19:47:53 +02:00
if (pointer) {
/*
* Struct defined inline, no name, need to have its
* members types emitted.
*/
if (type__name(tag__type(type)) == NULL)
2019-04-16 19:47:53 +02:00
type__emit_definitions(type, cu, emissions, fp);
return type__emit_fwd_decl(tag__type(type), emissions, fp);
2019-04-16 19:47:53 +02:00
}
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);
type_emissions__add_definition(emissions, ctype);
core: Use unnatural alignment of struct embedded in another to infer __packed__ Since we don't have something like DW_AT_alignment for __attribute__((__packed__)), we need to use whatever hints that are there in the alignments to figure out if a naturally packed struct has the __attribute__((packed)) in the original sources, because that is needed to waiver its natural alignment requisites. For instance, /* Used at: btrfs.c */ /* <1e7b> /home/acme/git/pahole/btrfs.c:199 */ struct btrfs_block_group_cache { struct btrfs_key key; /* 0 17 */ struct btrfs_block_group_item item; /* 17 24 */ /* XXX 7 bytes hole, try to pack */ struct btrfs_fs_info * fs_info; /* 48 8 */ struct inode * inode; /* 56 8 */ In the original source code, btrfs_block_group_item is marked __packed__, and being so, even seemingly unnecessarily, makes it, when embedded in another struct, like the above, forfeit its natural alingment, that would be 8 bytes, and instead appear right at the 17th byte offset... struct btrfs_block_group_item { __le64 used; /* 0 8 */ __le64 chunk_objectid; /* 8 8 */ __le64 flags; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ } __attribute__((__packed__)); So we need to, seeing its use at a unnatural offset, go backwards to the btrfs_block_group_item pahole internal data structure, 'struct type' and mark is_packed field as 'true', despite it not looking like a packed struct. Same thing with: struct ieee80211_mcs_info { u8 rx_mask[10]; /* 0 10 */ __le16 rx_highest; /* 10 2 */ u8 tx_params; /* 12 1 */ u8 reserved[3]; /* 13 3 */ /* size: 16, cachelines: 1, members: 4 */ /* last cacheline: 16 bytes */ }; That is naturally aligned and as 16 bytes, a power of two, then when it appears at the end of: $ pahole -IC ieee80211_sta_ht_cap vht.o /* Used at: vht.c */ /* <31ea> /home/acme/git/pahole/vht.c:1769 */ struct ieee80211_sta_ht_cap { u16 cap; /* 0 2 */ bool ht_supported; /* 2 1 */ u8 ampdu_factor; /* 3 1 */ u8 ampdu_density; /* 4 1 */ /* XXX 1 byte hole, try to pack */ struct ieee80211_mcs_info mcs; /* 6 16 */ /* size: 22, cachelines: 1, members: 5 */ /* sum members: 21, holes: 1, sum holes: 1 */ /* last cacheline: 22 bytes */ }; $ We get that one byte hole if ieee80211_mcs_info isn't marked __packed__, as soon as we mark it: $ pahole -IC ieee80211_sta_ht_cap vht.o /* Used at: vht.c */ /* <31ea> /home/acme/git/pahole/vht.c:1769 */ struct ieee80211_sta_ht_cap { u16 cap; /* 0 2 */ bool ht_supported; /* 2 1 */ u8 ampdu_factor; /* 3 1 */ u8 ampdu_density; /* 4 1 */ struct ieee80211_mcs_info mcs; /* 5 16 */ /* size: 22, cachelines: 1, members: 5 */ /* padding: 1 */ /* last cacheline: 22 bytes */ }; [acme@quaco pahole]$ It works, so __packed__ in this case just says: trow away the natural alignment, make it 1 in whatever container structs. So, before emitting the types for some struct, we go back looking at each of its members and checking for such unnatural offsets, marking the types as __packed__. Now: $ pfunct --compile /home/acme/git/build/v5.1-rc4+/net/mac80211/vht.o | grep "^struct ieee80211_mcs_info" -A8 struct ieee80211_mcs_info { u8 rx_mask[10]; /* 0 10 */ __le16 rx_highest; /* 10 2 */ u8 tx_params; /* 12 1 */ u8 reserved[3]; /* 13 3 */ /* size: 16, cachelines: 1, members: 4 */ /* last cacheline: 16 bytes */ } __attribute__((__packed__)); $ $ pfunct --compile /home/acme/git/build/v5.1-rc4+/fs/btrfs/free-space-tree.o | grep "^struct btrfs_block_group_item" -A7 struct btrfs_block_group_item { __le64 used; /* 0 8 */ __le64 chunk_objectid; /* 8 8 */ __le64 flags; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ } __attribute__((__packed__)); $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 20:51:16 +02:00
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);
}
}