Blame generate.c

Packit 400c17
/*
Packit 400c17
	Copyright(C) 2016, Red Hat, Inc., Stanislav Kozina
Packit 400c17
Packit 400c17
	This program is free software: you can redistribute it and/or modify
Packit 400c17
	it under the terms of the GNU General Public License as published by
Packit 400c17
	the Free Software Foundation, either version 3 of the License, or
Packit 400c17
	(at your option) any later version.
Packit 400c17
Packit 400c17
	This program is distributed in the hope that it will be useful,
Packit 400c17
	but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 400c17
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 400c17
	GNU General Public License for more details.
Packit 400c17
Packit 400c17
	You should have received a copy of the GNU General Public License
Packit 400c17
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 400c17
*/
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * This file contains the code which generates the kabi information for the
Packit 400c17
 * given build of the Linux kernel.
Packit 400c17
 */
Packit 400c17
Packit 400c17
#include <dwarf.h>
Packit 400c17
#include <inttypes.h>
Packit 400c17
#include <ctype.h>
Packit 400c17
#include <libelf.h>
Packit 400c17
#include <fcntl.h>
Packit 400c17
#include <stdio.h>
Packit 400c17
#include <string.h>
Packit 400c17
#include <sys/types.h>
Packit 400c17
#include <sys/stat.h>
Packit 400c17
#include <unistd.h>
Packit 400c17
#include <stdlib.h>
Packit 400c17
#include <errno.h>
Packit 400c17
#include <assert.h>
Packit 400c17
#include <getopt.h>
Packit 400c17
#include <limits.h>
Packit 400c17
Packit 400c17
#include <elfutils/libdw.h>
Packit 400c17
#include <elfutils/libdwfl.h>
Packit 400c17
#include <elfutils/known-dwarf.h>
Packit 400c17
Packit 400c17
#include "main.h"
Packit 400c17
#include "utils.h"
Packit 400c17
#include "generate.h"
Packit 400c17
#include "ksymtab.h"
Packit 400c17
#include "stack.h"
Packit 400c17
#include "hash.h"
Packit 400c17
#include "objects.h"
Packit 400c17
#include "list.h"
Packit 400c17
Packit 400c17
#define	EMPTY_NAME	"(NULL)"
Packit 400c17
#define PROCESSED_SIZE 1024
Packit 400c17
/*
Packit 400c17
  DB size is number of hash buckets, do not have to be exact,
Packit 400c17
  but since now we have ~20K records, make it this
Packit 400c17
*/
Packit 400c17
#define DB_SIZE (20 * 1024)
Packit 400c17
#define INITIAL_RECORD_SIZE 512
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * Dwarf5 spec, 7.5.4 Attribute Encodings
Packit 400c17
 * libdw doesn't support it yet
Packit 400c17
 */
Packit 400c17
#ifndef DW_AT_alignment
Packit 400c17
#define DW_AT_alignment 0x88
Packit 400c17
#endif
Packit 400c17
Packit 400c17
struct set;
Packit 400c17
struct record_db;
Packit 400c17
Packit 400c17
typedef struct {
Packit 400c17
	char *kernel_dir; /* Path to  the kernel modules to process */
Packit 400c17
	char *kabi_dir; /* Where to put the output */
Packit 400c17
	struct ksymtab *symbols; /* List of symbols to generate */
Packit 400c17
	size_t symbol_cnt;
Packit 400c17
	struct record_db *db;
Packit 400c17
	bool rhel_tree;
Packit 400c17
	bool verbose;
Packit 400c17
	bool gen_extra;
Packit 400c17
} generate_config_t;
Packit 400c17
Packit 400c17
struct cu_ctx {
Packit 400c17
	generate_config_t *conf;
Packit 400c17
	Dwarf_Die *cu_die;
Packit 400c17
	stack_t *stack; /* Current stack of symbol we're parsing */
Packit 400c17
	struct set *processed; /* Set of processed types for this CU */
Packit 400c17
};
Packit 400c17
Packit 400c17
struct file_ctx {
Packit 400c17
	generate_config_t *conf;
Packit 400c17
	struct ksymtab *ksymtab; /* ksymtab of the current kernel module */
Packit 400c17
};
Packit 400c17
Packit 400c17
struct dwarf_type {
Packit 400c17
	unsigned int dwarf_tag;
Packit 400c17
	char *prefix;
Packit 400c17
} known_dwarf_types[] = {
Packit 400c17
	{ DW_TAG_subprogram, FUNC_FILE },
Packit 400c17
	{ DW_TAG_typedef, TYPEDEF_FILE },
Packit 400c17
	{ DW_TAG_variable, VAR_FILE },
Packit 400c17
	{ DW_TAG_enumeration_type, ENUM_FILE },
Packit 400c17
	{ DW_TAG_structure_type, STRUCT_FILE },
Packit 400c17
	{ DW_TAG_union_type, UNION_FILE },
Packit 400c17
	{ 0, NULL }
Packit 400c17
};
Packit 400c17
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * Structure of the database record:
Packit 400c17
 *
Packit 400c17
 * key: record key, usually includes path the file, where the type is
Packit 400c17
 *      defined (may include pseudo path, like <declaration>);
Packit 400c17
 *
Packit 400c17
 * version: type's version, used when we need to add another type of the same
Packit 400c17
 *	    name. It may happend, for example, when because of defines the same
Packit 400c17
 *          structure has changed for different compilation units.
Packit 400c17
 *
Packit 400c17
 *          It is not for the case, when the same structure defined in
Packit 400c17
 *	    different files -- it will have different keys, since it includes
Packit 400c17
 *	    the path;
Packit 400c17
 *
Packit 400c17
 * ref_count: reference counter, needed since the ownership is shared with the
Packit 400c17
 *            internal database;
Packit 400c17
 *
Packit 400c17
 * base_file: base part of the key (without version), used to generate the
Packit 400c17
 *            unique key for the new version;
Packit 400c17
 *
Packit 400c17
 * cu: compilation unit, where the type for the record defined;
Packit 400c17
 *
Packit 400c17
 * origin: "File <file>:<line>" string, describing the source, where the type
Packit 400c17
 *         for the record defined;
Packit 400c17
 *
Packit 400c17
 * stack: stack of types to reach this one.
Packit 400c17
 *         Ex.: on the toplevel
Packit 400c17
 *              struct A {
Packit 400c17
 *                        struct B fieldA;
Packit 400c17
 *              }
Packit 400c17
 *         in another file:
Packit 400c17
 *              struct B {
Packit 400c17
 *                        basetype fieldB;
Packit 400c17
 *              }
Packit 400c17
 *         the "struct B" description will contain key of the "struct A"
Packit 400c17
 *         description record in the stack;
Packit 400c17
 *
Packit 400c17
 * obj: pointer to the abstract type object, representing the toplevel type of
Packit 400c17
 *      the record.
Packit 400c17
 *
Packit 400c17
 * link: name of weak link alisas for the weak aliases.
Packit 400c17
 *
Packit 400c17
 * free: type specific function to free the record
Packit 400c17
 *       (there are normal, weak and assembly records).
Packit 400c17
 *
Packit 400c17
 * dump: type specific function for record output.
Packit 400c17
 *
Packit 400c17
 * dependents: objects that reference this record.
Packit 400c17
 */
Packit 400c17
struct record {
Packit 400c17
	char *key;
Packit 400c17
	int version;
Packit 400c17
	int ref_count;
Packit 400c17
	char *base_file;
Packit 400c17
	char *cu;
Packit 400c17
	char *origin;
Packit 400c17
	stack_t *stack;
Packit 400c17
	obj_t *obj;
Packit 400c17
	char *link;
Packit 400c17
	void (*free)(struct record *);
Packit 400c17
	void (*dump)(struct record *, FILE *);
Packit 400c17
Packit 400c17
	struct list dependents;
Packit 400c17
};
Packit 400c17
Packit 400c17
void record_update_dependents(struct record *record)
Packit 400c17
{
Packit 400c17
	struct list_node *iter;
Packit 400c17
Packit 400c17
	LIST_FOR_EACH(&record->dependents, iter) {
Packit 400c17
		obj_t *obj = list_node_data(iter);
Packit 400c17
Packit 400c17
		free(obj->base_type);
Packit 400c17
		obj->base_type = safe_strdup(record->key);
Packit 400c17
	}
Packit 400c17
}
Packit 400c17
Packit 400c17
static const bool is_builtin(Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	char *fname;
Packit 400c17
	const char *path = dwarf_decl_file(die);
Packit 400c17
Packit 400c17
	if (path == NULL)
Packit 400c17
		return true;
Packit 400c17
Packit 400c17
	fname = basename(path);
Packit 400c17
	assert (fname != NULL);
Packit 400c17
	if (strcmp(fname, "<built-in>") == 0)
Packit 400c17
		return true;
Packit 400c17
Packit 400c17
	return false;
Packit 400c17
}
Packit 400c17
Packit 400c17
static const char *get_die_name(Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	if (dwarf_hasattr(die, DW_AT_name))
Packit 400c17
		return dwarf_diename(die);
Packit 400c17
	else
Packit 400c17
		return EMPTY_NAME;
Packit 400c17
}
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * Check if given DIE has DW_AT_declaration attribute.
Packit 400c17
 * That indicates that the symbol is just a declaration, not full definition.
Packit 400c17
 */
Packit 400c17
static bool is_declaration(Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	Dwarf_Attribute attr;
Packit 400c17
Packit 400c17
	if (!dwarf_hasattr(die, DW_AT_declaration))
Packit 400c17
		return false;
Packit 400c17
	(void) dwarf_attr(die, DW_AT_declaration, &attr);
Packit 400c17
	if (!dwarf_hasform(&attr, DW_FORM_flag_present))
Packit 400c17
		return false;
Packit 400c17
	return true;
Packit 400c17
}
Packit 400c17
Packit 400c17
static char *get_file_replace_path;
Packit 400c17
Packit 400c17
static char *_get_file(Dwarf_Die *cu_die, Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	const char *filename;
Packit 400c17
	char *ret;
Packit 400c17
Packit 400c17
	filename = dwarf_decl_file(die);
Packit 400c17
Packit 400c17
	if (get_file_replace_path) {
Packit 400c17
		int len = strlen(get_file_replace_path);
Packit 400c17
Packit 400c17
		if (strncmp(filename, get_file_replace_path, len) == 0) {
Packit 400c17
			filename = filename + len;
Packit 400c17
			while (*filename == '/')
Packit 400c17
				filename++;
Packit 400c17
		}
Packit 400c17
	}
Packit 400c17
Packit 400c17
Packit 400c17
	ret = safe_strdup(filename);
Packit 400c17
	path_normalize(ret);
Packit 400c17
Packit 400c17
	return ret;
Packit 400c17
}
Packit 400c17
Packit 400c17
static char *get_file(Dwarf_Die *cu_die, Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	Dwarf_Attribute attr;
Packit 400c17
	Dwarf_Die spec_die;
Packit 400c17
Packit 400c17
	/*
Packit 400c17
	 * Handle types built-in in C compiler. These are for example the
Packit 400c17
	 * variable argument list which is defined as * struct __va_list_tag.
Packit 400c17
	 */
Packit 400c17
	if (is_builtin(die))
Packit 400c17
		return safe_strdup(BUILTIN_PATH);
Packit 400c17
Packit 400c17
	if (dwarf_hasattr(die, DW_AT_decl_file))
Packit 400c17
		return _get_file(cu_die, die);
Packit 400c17
Packit 400c17
	if ((dwarf_attr(die, DW_AT_specification, &attr) == NULL) ||
Packit 400c17
	    (dwarf_formref_die(&attr, &spec_die) == NULL)) {
Packit 400c17
		fail("DIE missing file information: %s\n",
Packit 400c17
		     dwarf_diename(die));
Packit 400c17
	}
Packit 400c17
Packit 400c17
	return _get_file(cu_die, &spec_die);
Packit 400c17
}
Packit 400c17
Packit 400c17
static long get_line(Dwarf_Die *cu_die, Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	Dwarf_Attribute attr;
Packit 400c17
	Dwarf_Word line;
Packit 400c17
	Dwarf_Die spec_die;
Packit 400c17
Packit 400c17
	if (is_builtin(die))
Packit 400c17
		return 0;
Packit 400c17
Packit 400c17
	if (dwarf_attr(die, DW_AT_decl_line, &attr) != NULL) {
Packit 400c17
		dwarf_formudata(&attr, &line);
Packit 400c17
		return line;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	if ((dwarf_attr(die, DW_AT_specification, &attr) == NULL) ||
Packit 400c17
	    (dwarf_formref_die(&attr, &spec_die) == NULL)) {
Packit 400c17
		fail("DIE missing line information: %s\n",
Packit 400c17
		     dwarf_diename(die));
Packit 400c17
	}
Packit 400c17
Packit 400c17
	return get_line(cu_die, &spec_die);
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *print_die(struct cu_ctx *, struct record *, Dwarf_Die *);
Packit 400c17
Packit 400c17
static const char *dwarf_tag_string(unsigned int tag)
Packit 400c17
{
Packit 400c17
	switch (tag) {
Packit 400c17
#define	DWARF_ONE_KNOWN_DW_TAG(NAME, CODE) case CODE: return #NAME;
Packit 400c17
		DWARF_ALL_KNOWN_DW_TAG
Packit 400c17
#undef DWARF_ONE_KNOWN_DW_TAG
Packit 400c17
	default:
Packit 400c17
		return NULL;
Packit 400c17
	}
Packit 400c17
}
Packit 400c17
Packit 400c17
static char *get_file_prefix(unsigned int dwarf_tag)
Packit 400c17
{
Packit 400c17
	struct dwarf_type *current;
Packit 400c17
Packit 400c17
	for (current = known_dwarf_types; current->prefix != NULL; current++) {
Packit 400c17
		if (dwarf_tag == current->dwarf_tag)
Packit 400c17
			break;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	return current->prefix;
Packit 400c17
}
Packit 400c17
Packit 400c17
static char *get_symbol_file(Dwarf_Die *die, Dwarf_Die *cu_die)
Packit 400c17
{
Packit 400c17
	const char *name = dwarf_diename(die);
Packit 400c17
	unsigned int tag = dwarf_tag(die);
Packit 400c17
	char *file_prefix;
Packit 400c17
	char *file_name = NULL;
Packit 400c17
Packit 400c17
	file_prefix = get_file_prefix(tag);
Packit 400c17
	if (file_prefix == NULL) {
Packit 400c17
		/* No need to redirect output for this type */
Packit 400c17
		return NULL;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	/*
Packit 400c17
	 * DW_AT_declaration don't have DW_AT_decl_file.
Packit 400c17
	 * Pretend like it's in other, non existent file.
Packit 400c17
	 */
Packit 400c17
	if (is_declaration(die)) {
Packit 400c17
		safe_asprintf(&file_name, DECLARATION_PATH "/%s%s.txt",
Packit 400c17
		    file_prefix, name);
Packit 400c17
Packit 400c17
		return file_name;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	/*
Packit 400c17
	 * Following types can be anonymous, eg. used directly as variable type
Packit 400c17
	 * in the declaration. We don't create new file for them if that's
Packit 400c17
	 * the case, embed them directly in the current file.
Packit 400c17
	 * Note that anonymous unions can also be embedded directly in the
Packit 400c17
	 * structure!
Packit 400c17
	 */
Packit 400c17
	switch (tag) {
Packit 400c17
	case DW_TAG_enumeration_type:
Packit 400c17
	case DW_TAG_structure_type:
Packit 400c17
	case DW_TAG_union_type:
Packit 400c17
		if (name == NULL)
Packit 400c17
			return NULL;
Packit 400c17
		break;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	/* We don't expect our name to be empty now */
Packit 400c17
	assert(name != NULL);
Packit 400c17
Packit 400c17
	safe_asprintf(&file_name, "%s%s.txt", file_prefix, name);
Packit 400c17
Packit 400c17
	return file_name;
Packit 400c17
}
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * Check if given DIE is external.
Packit 400c17
 * It can has DW_AT_external attribute itself,
Packit 400c17
 * or in case of reference to the specification,
Packit 400c17
 * the specification DIE can be exported.
Packit 400c17
 */
Packit 400c17
static int is_external(Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	Dwarf_Attribute attr;
Packit 400c17
	Dwarf_Die spec_die;
Packit 400c17
Packit 400c17
	if (dwarf_hasattr(die, DW_AT_external)) {
Packit 400c17
		dwarf_attr(die, DW_AT_external, &attr);
Packit 400c17
		if (!dwarf_hasform(&attr, DW_FORM_flag_present))
Packit 400c17
			return false;
Packit 400c17
		return true;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	if (dwarf_attr(die, DW_AT_specification, &attr) == NULL)
Packit 400c17
		return false;
Packit 400c17
Packit 400c17
	if (dwarf_formref_die(&attr, &spec_die) == NULL)
Packit 400c17
		fail("dwarf_formref_die() failed for %s\n",
Packit 400c17
		     dwarf_diename(die));
Packit 400c17
	return is_external(&spec_die);
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *die_read_alignment(Dwarf_Die *die, obj_t *obj)
Packit 400c17
{
Packit 400c17
	Dwarf_Attribute attr;
Packit 400c17
	Dwarf_Word value;
Packit 400c17
Packit 400c17
	if (dwarf_attr(die, DW_AT_alignment, &attr) == NULL)
Packit 400c17
		goto out;
Packit 400c17
Packit 400c17
	dwarf_formudata(&attr, &value);
Packit 400c17
	obj->alignment = value;
Packit 400c17
out:
Packit 400c17
	return obj;
Packit 400c17
}
Packit 400c17
Packit 400c17
struct set *set_init(size_t size)
Packit 400c17
{
Packit 400c17
	struct hash *h;
Packit 400c17
Packit 400c17
	h = hash_new(size, free);
Packit 400c17
	if (h == NULL)
Packit 400c17
		fail("Cannot create hash");
Packit 400c17
Packit 400c17
	return (struct set *)h;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void set_add(struct set *set, const char *key)
Packit 400c17
{
Packit 400c17
	char *storage;
Packit 400c17
	struct hash *h = (struct hash *)set;
Packit 400c17
Packit 400c17
	storage = safe_strdup(key);
Packit 400c17
	hash_add(h, storage, storage);
Packit 400c17
}
Packit 400c17
Packit 400c17
static bool set_contains(struct set *set, const char *key)
Packit 400c17
{
Packit 400c17
	void *v;
Packit 400c17
	struct hash *h = (struct hash *)set;
Packit 400c17
Packit 400c17
	v = hash_find(h, key);
Packit 400c17
	return v != NULL;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void set_free(struct set *set)
Packit 400c17
{
Packit 400c17
	struct hash *h = (struct hash *)set;
Packit 400c17
Packit 400c17
	hash_free(h);
Packit 400c17
}
Packit 400c17
Packit 400c17
static struct record *record_alloc(void)
Packit 400c17
{
Packit 400c17
	struct record *rec;
Packit 400c17
Packit 400c17
	rec = safe_zmalloc(sizeof(*rec));
Packit 400c17
	return rec;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_free_regular(struct record *rec)
Packit 400c17
{
Packit 400c17
	void *data;
Packit 400c17
	struct list_node *iter;
Packit 400c17
Packit 400c17
	free(rec->base_file);
Packit 400c17
	free(rec->origin);
Packit 400c17
	if (rec->cu)
Packit 400c17
		free(rec->cu);
Packit 400c17
Packit 400c17
	while ((data = stack_pop(rec->stack)) != NULL)
Packit 400c17
		free(data);
Packit 400c17
	stack_destroy(rec->stack);
Packit 400c17
Packit 400c17
	LIST_FOR_EACH(&rec->dependents, iter) {
Packit 400c17
		obj_t *o = list_node_data(iter);
Packit 400c17
Packit 400c17
		o->depend_rec_node = NULL;
Packit 400c17
	}
Packit 400c17
	list_clear(&rec->dependents);
Packit 400c17
Packit 400c17
	obj_free(rec->obj);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_free_weak(struct record *rec)
Packit 400c17
{
Packit 400c17
	free(rec->link);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_free(struct record *rec)
Packit 400c17
{
Packit 400c17
	free(rec->key);
Packit 400c17
Packit 400c17
	if (rec->free)
Packit 400c17
		rec->free(rec);
Packit 400c17
	free(rec);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_put(struct record *rec)
Packit 400c17
{
Packit 400c17
	assert(rec->ref_count > 0);
Packit 400c17
Packit 400c17
	if (--rec->ref_count == 0)
Packit 400c17
		record_free(rec);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_get(struct record *rec)
Packit 400c17
{
Packit 400c17
	rec->ref_count++;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_dump_regular(struct record *rec, FILE *f);
Packit 400c17
Packit 400c17
static struct record *record_new_regular(const char *key)
Packit 400c17
{
Packit 400c17
	struct record *rec;
Packit 400c17
Packit 400c17
	rec = record_alloc();
Packit 400c17
	rec->key = safe_strdup(key);
Packit 400c17
	rec->stack = stack_init();
Packit 400c17
	rec->free = record_free_regular;
Packit 400c17
	rec->dump = record_dump_regular;
Packit 400c17
	list_init(&rec->dependents, NULL);
Packit 400c17
	record_get(rec);
Packit 400c17
	return rec;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_dump_assembly(struct record *rec, FILE *f);
Packit 400c17
Packit 400c17
static struct record *record_new_assembly(const char *key)
Packit 400c17
{
Packit 400c17
	struct record *rec;
Packit 400c17
Packit 400c17
	rec = record_alloc();
Packit 400c17
	rec->key = safe_strdup(key);
Packit 400c17
Packit 400c17
	/*
Packit 400c17
	 * The symbol not necessary belongs to an assembly function,
Packit 400c17
	 * it is actually "no definition found in the debug information",
Packit 400c17
	 * but the main goal is to find the assembly ones.
Packit 400c17
	 */
Packit 400c17
Packit 400c17
	rec->free = NULL;
Packit 400c17
	rec->dump = record_dump_assembly;
Packit 400c17
Packit 400c17
	record_get(rec);
Packit 400c17
Packit 400c17
	return rec;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_dump_weak(struct record *rec, FILE *f);
Packit 400c17
Packit 400c17
static struct record *record_new_weak(const char *key, const char *link)
Packit 400c17
{
Packit 400c17
	struct record *rec;
Packit 400c17
Packit 400c17
	rec = record_alloc();
Packit 400c17
	rec->key = safe_strdup(key);
Packit 400c17
	rec->link = safe_strdup(link);
Packit 400c17
Packit 400c17
	rec->free = record_free_weak;
Packit 400c17
	rec->dump = record_dump_weak;
Packit 400c17
Packit 400c17
	record_get(rec);
Packit 400c17
Packit 400c17
	return rec;
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *record_obj(struct record *rec)
Packit 400c17
{
Packit 400c17
	return rec->obj;
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *record_obj_exchange(struct record *rec, obj_t *o)
Packit 400c17
{
Packit 400c17
	obj_t *old;
Packit 400c17
Packit 400c17
	old = rec->obj;
Packit 400c17
	rec->obj = o;
Packit 400c17
	return old;
Packit 400c17
}
Packit 400c17
Packit 400c17
static const char *record_origin(struct record *rec)
Packit 400c17
{
Packit 400c17
	return rec->origin;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void copy_stack_cb(void *data, void *arg)
Packit 400c17
{
Packit 400c17
	char *symbol = (char *)data;
Packit 400c17
	struct record *fp = (struct record *)arg;
Packit 400c17
	char *copy;
Packit 400c17
Packit 400c17
	copy = safe_strdup(symbol);
Packit 400c17
	stack_push(fp->stack, copy);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_add_stack(struct record *rec, stack_t *stack)
Packit 400c17
{
Packit 400c17
	walk_stack_backward(stack, copy_stack_cb, rec);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_add_cu(struct record *rec, Dwarf_Die *cu_die)
Packit 400c17
{
Packit 400c17
	const char *name;
Packit 400c17
Packit 400c17
	if (cu_die == NULL)
Packit 400c17
		return;
Packit 400c17
Packit 400c17
	name = dwarf_diename(cu_die);
Packit 400c17
	safe_asprintf(&rec->cu, "CU: \"%s\"\n", name);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_add_origin(struct record *rec,
Packit 400c17
			      Dwarf_Die *cu_die,
Packit 400c17
			      Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	char *dec_file;
Packit 400c17
	long dec_line;
Packit 400c17
Packit 400c17
	dec_file = get_file(cu_die, die);
Packit 400c17
	dec_line = get_line(cu_die, die);
Packit 400c17
Packit 400c17
	safe_asprintf(&rec->origin, "File: %s:%lu\n", dec_file, dec_line);
Packit 400c17
	free(dec_file);
Packit 400c17
}
Packit 400c17
Packit 400c17
static struct record *record_start(struct cu_ctx *ctx,
Packit 400c17
				   Dwarf_Die *die,
Packit 400c17
				   char *key)
Packit 400c17
{
Packit 400c17
	struct record *rec = NULL;
Packit 400c17
	generate_config_t *conf = ctx->conf;
Packit 400c17
	Dwarf_Die *cu_die = ctx->cu_die;
Packit 400c17
Packit 400c17
	/*
Packit 400c17
	 * Don't try to reenter a file that we have seen already for
Packit 400c17
	 * this CU.
Packit 400c17
	 * Note that this is just a pure optimization, the same file
Packit 400c17
	 * (type) in the same CU must be identical.
Packit 400c17
	 * But this is major optimization, without it a single
Packit 400c17
	 * re-generation of a top file would require a full regeneration
Packit 400c17
	 * of its full tree, thus the difference in speed is many orders
Packit 400c17
	 * of magnitude!
Packit 400c17
	 */
Packit 400c17
	if (set_contains(ctx->processed, key))
Packit 400c17
		goto done;
Packit 400c17
Packit 400c17
	set_add(ctx->processed, key);
Packit 400c17
Packit 400c17
	if (is_declaration(die)) {
Packit 400c17
		if (conf->verbose)
Packit 400c17
			printf("WARNING: Skipping following file as we "
Packit 400c17
			       "have only declaration: %s\n", key);
Packit 400c17
		goto done;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	if (conf->verbose)
Packit 400c17
		printf("Generating %s\n", key);
Packit 400c17
Packit 400c17
	rec = record_new_regular(key);
Packit 400c17
Packit 400c17
	if (conf->gen_extra)
Packit 400c17
		record_add_cu(rec, cu_die);
Packit 400c17
	record_add_origin(rec, cu_die, die);
Packit 400c17
	record_add_stack(rec, ctx->stack);
Packit 400c17
done:
Packit 400c17
	return rec;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_set_version(struct record *rec, int version)
Packit 400c17
{
Packit 400c17
	char *base_file = rec->base_file;
Packit 400c17
	char *key = NULL;
Packit 400c17
Packit 400c17
	if (version == 0)
Packit 400c17
		return;
Packit 400c17
Packit 400c17
	if (rec->version == 0) {
Packit 400c17
		base_file = safe_strdup(rec->key);
Packit 400c17
		/* Remove .txt ending */
Packit 400c17
		base_file[strlen(base_file) - 4] = '\0';
Packit 400c17
		rec->base_file = base_file;
Packit 400c17
	}
Packit 400c17
	rec->version = version;
Packit 400c17
	safe_asprintf(&key, "%s-%i.txt", base_file, rec->version);
Packit 400c17
	free(rec->key);
Packit 400c17
	rec->key = key;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_close(struct record *rec, obj_t *obj)
Packit 400c17
{
Packit 400c17
	obj_fill_parent(obj);
Packit 400c17
	rec->obj = obj;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_stack_dump_and_clear(struct record *rec, FILE *f)
Packit 400c17
{
Packit 400c17
	char *data = stack_pop(rec->stack);
Packit 400c17
Packit 400c17
	if (data == NULL)
Packit 400c17
		return;
Packit 400c17
Packit 400c17
	fprintf(f, "Stack:\n");
Packit 400c17
	do {
Packit 400c17
		fprintf(f, "-> \"%s\"\n", data);
Packit 400c17
		free(data);
Packit 400c17
	} while ((data = stack_pop(rec->stack)) != NULL);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_dump_regular(struct record *rec, FILE *f)
Packit 400c17
{
Packit 400c17
	int rc;
Packit 400c17
Packit 400c17
	fprintf(f, FILEFMT_VERSION_STRING);
Packit 400c17
	if (rec->cu != NULL) {
Packit 400c17
		rc = fputs(rec->cu, f);
Packit 400c17
		if (rc == EOF)
Packit 400c17
			fail("Could not put CU name");
Packit 400c17
	}
Packit 400c17
	rc = fputs(rec->origin, f);
Packit 400c17
	if (rc == EOF)
Packit 400c17
		fail("Could not put origin");
Packit 400c17
Packit 400c17
	record_stack_dump_and_clear(rec, f);
Packit 400c17
Packit 400c17
	fprintf(f, "Symbol:\n");
Packit 400c17
	if (rec->obj->alignment != 0)
Packit 400c17
		fprintf(f, "Alignment %u\n", rec->obj->alignment);
Packit 400c17
Packit 400c17
	obj_dump(rec->obj, f);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_dump_assembly(struct record *rec, FILE *f)
Packit 400c17
{
Packit 400c17
	char *name = filenametosymbol(rec->key);
Packit 400c17
	int rc;
Packit 400c17
Packit 400c17
	rc = fprintf(f, FILEFMT_VERSION_STRING "Symbol:\nassembly %s\n", name);
Packit 400c17
	free(name);
Packit 400c17
	if (rc < 0)
Packit 400c17
		fail("Could not put assembly\n");
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_dump_weak(struct record *rec, FILE *f)
Packit 400c17
{
Packit 400c17
	char *name = filenametosymbol(rec->key);
Packit 400c17
	int rc;
Packit 400c17
Packit 400c17
	rc = fprintf(f, FILEFMT_VERSION_STRING "Symbol:\nweak %s -> %s\n",
Packit 400c17
		     name, rec->link);
Packit 400c17
	free(name);
Packit 400c17
	if (rc < 0)
Packit 400c17
		fail("Could not put weak link\n");
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_dump(struct record *rec, const char *dir)
Packit 400c17
{
Packit 400c17
	char path[PATH_MAX];
Packit 400c17
	FILE *f;
Packit 400c17
	char *slash;
Packit 400c17
Packit 400c17
	snprintf(path, sizeof(path), "%s/%s", dir, rec->key);
Packit 400c17
Packit 400c17
	slash = strrchr(path, '/');
Packit 400c17
	assert(slash != NULL);
Packit 400c17
	*slash = '\0';
Packit 400c17
	rec_mkdir(path);
Packit 400c17
	*slash = '/';
Packit 400c17
Packit 400c17
	f = fopen(path, "w");
Packit 400c17
	if (f == NULL)
Packit 400c17
		fail("Cannot create record file '%s': %m", path);
Packit 400c17
Packit 400c17
	rec->dump(rec, f);
Packit 400c17
Packit 400c17
	fclose(f);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void list_record_free(void *value)
Packit 400c17
{
Packit 400c17
	struct record *rec = value;
Packit 400c17
Packit 400c17
	record_put(rec);
Packit 400c17
}
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * merge rec_src to the record rec_dst
Packit 400c17
 */
Packit 400c17
static bool record_merge(struct record *rec_dst,
Packit 400c17
			 struct record *rec_src,
Packit 400c17
			 bool merge_decl)
Packit 400c17
{
Packit 400c17
	const char *s1;
Packit 400c17
	const char *s2;
Packit 400c17
	obj_t *o1;
Packit 400c17
	obj_t *o2;
Packit 400c17
	obj_t *o;
Packit 400c17
Packit 400c17
	s1 = record_origin(rec_dst);
Packit 400c17
	s2 = record_origin(rec_src);
Packit 400c17
Packit 400c17
	if (!safe_streq(s1, s2))
Packit 400c17
		return false;
Packit 400c17
Packit 400c17
	o1 = record_obj(rec_dst);
Packit 400c17
	o2 = record_obj(rec_src);
Packit 400c17
Packit 400c17
	o = obj_merge(o1, o2, merge_decl);
Packit 400c17
	if (o == NULL)
Packit 400c17
		return false;
Packit 400c17
Packit 400c17
	obj_fill_parent(o);
Packit 400c17
	o = record_obj_exchange(rec_dst, o);
Packit 400c17
	obj_free(o);
Packit 400c17
Packit 400c17
	return true;
Packit 400c17
}
Packit 400c17
Packit 400c17
static char *record_db_add(struct record_db *db, struct record *rec)
Packit 400c17
{
Packit 400c17
	/*
Packit 400c17
	 * Now we need to put the new type record we've just generated
Packit 400c17
	 * to the db.
Packit 400c17
	 *
Packit 400c17
	 * The problem stopping us from merging every record possible is that
Packit 400c17
	 * multiple records can't be merged if it is not possible to create
Packit 400c17
	 * groups in which they should be merged. This is not a problem if all
Packit 400c17
	 * the records are able to be merged together, but when there is one or
Packit 400c17
	 * more incompatible merging then merging them aggressively could group
Packit 400c17
	 * them in the wrong way. And because the right way to group them can't
Packit 400c17
	 * be decided, it is better not to group and subsequently merge them at
Packit 400c17
	 * all.
Packit 400c17
	 *
Packit 400c17
	 * Meaning that at this stage, when we don't know all the records, we
Packit 400c17
	 * can't decide if all records can be merged into one, and so the only
Packit 400c17
	 * records we can merge are those that are completely same.
Packit 400c17
	 *
Packit 400c17
	 * In case they aren't same we store them as another node of the list.
Packit 400c17
	 * We also change the name of the new record, so that two reffile obj_t
Packit 400c17
	 * referencing records that we couldn't merge wouldn't get merged.
Packit 400c17
	 */
Packit 400c17
Packit 400c17
	struct hash *hash = (struct hash *)db;
Packit 400c17
	struct record *tmp_rec;
Packit 400c17
	struct list *list;
Packit 400c17
	struct list_node *iter;
Packit 400c17
Packit 400c17
	list = hash_find(hash, rec->key);
Packit 400c17
	if (list == NULL) {
Packit 400c17
		list = list_new(list_record_free);
Packit 400c17
Packit 400c17
		hash_add(hash, rec->key, list);
Packit 400c17
	}
Packit 400c17
Packit 400c17
	LIST_FOR_EACH(list, iter) {
Packit 400c17
		tmp_rec = list_node_data(iter);
Packit 400c17
Packit 400c17
		if (record_merge(tmp_rec, rec, NO_MERGE_DECL)) {
Packit 400c17
			list_concat(&tmp_rec->dependents, &rec->dependents);
Packit 400c17
			return safe_strdup(tmp_rec->key);
Packit 400c17
		}
Packit 400c17
	}
Packit 400c17
Packit 400c17
	record_get(rec);
Packit 400c17
	record_set_version(rec, list->len);
Packit 400c17
	list_add(list, rec);
Packit 400c17
Packit 400c17
	return safe_strdup(rec->key);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void hash_list_free(void *value)
Packit 400c17
{
Packit 400c17
	struct list *list = value;
Packit 400c17
Packit 400c17
	list_free(list);
Packit 400c17
}
Packit 400c17
Packit 400c17
static struct record_db *record_db_init(void)
Packit 400c17
{
Packit 400c17
	struct hash *db;
Packit 400c17
Packit 400c17
	db = hash_new(DB_SIZE, hash_list_free);
Packit 400c17
	if (db == NULL)
Packit 400c17
		fail("Could not create db (hash)\n");
Packit 400c17
Packit 400c17
	return (struct record_db *)db;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_db_dump(struct record_db *_db, char *dir)
Packit 400c17
{
Packit 400c17
	struct hash_iter iter;
Packit 400c17
	const void *v;
Packit 400c17
	struct hash *db = (struct hash *)_db;
Packit 400c17
Packit 400c17
	hash_iter_init(db, &iter);
Packit 400c17
	while (hash_iter_next(&iter, NULL, &v)) {
Packit 400c17
		struct list *list = (struct list *)v;
Packit 400c17
		struct list_node *iter;
Packit 400c17
Packit 400c17
		LIST_FOR_EACH(list, iter) {
Packit 400c17
			struct record *rec = list_node_data(iter);
Packit 400c17
Packit 400c17
			record_dump(rec, dir);
Packit 400c17
		}
Packit 400c17
	}
Packit 400c17
}
Packit 400c17
Packit 400c17
static void record_db_free(struct record_db *_db)
Packit 400c17
{
Packit 400c17
	struct hash *db = (struct hash *)_db;
Packit 400c17
Packit 400c17
	hash_free(db);
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *print_die_type(struct cu_ctx *ctx,
Packit 400c17
			     struct record *rec,
Packit 400c17
			     Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	Dwarf_Die type_die;
Packit 400c17
	Dwarf_Attribute attr;
Packit 400c17
Packit 400c17
	if (!dwarf_hasattr(die, DW_AT_type))
Packit 400c17
		return obj_basetype_new(safe_strdup("void"));
Packit 400c17
Packit 400c17
	(void) dwarf_attr(die, DW_AT_type, &attr);
Packit 400c17
	if (dwarf_formref_die(&attr, &type_die) == NULL)
Packit 400c17
		fail("dwarf_formref_die() failed for %s\n",
Packit 400c17
		    dwarf_diename(die));
Packit 400c17
Packit 400c17
	/* Print the type of the die */
Packit 400c17
	return print_die(ctx, rec, &type_die);
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *print_die_struct_member(struct cu_ctx *ctx,
Packit 400c17
				      struct record *rec,
Packit 400c17
				      Dwarf_Die *die,
Packit 400c17
				      const char *name)
Packit 400c17
{
Packit 400c17
	Dwarf_Attribute attr;
Packit 400c17
	Dwarf_Word value;
Packit 400c17
	obj_t *type;
Packit 400c17
	obj_t *obj;
Packit 400c17
Packit 400c17
	if (dwarf_attr(die, DW_AT_data_member_location, &attr) == NULL)
Packit 400c17
		fail("Offset of member %s missing!\n", name);
Packit 400c17
Packit 400c17
	(void) dwarf_formudata(&attr, &value);
Packit 400c17
Packit 400c17
	type = print_die_type(ctx, rec, die);
Packit 400c17
	obj = obj_struct_member_new_add(safe_strdup(name), type);
Packit 400c17
	obj->offset = value;
Packit 400c17
Packit 400c17
	if (dwarf_hasattr(die, DW_AT_bit_offset)) {
Packit 400c17
		Dwarf_Word offset, size;
Packit 400c17
Packit 400c17
		if (!dwarf_hasattr(die, DW_AT_bit_size))
Packit 400c17
			fail("Missing expected bit size attribute in %s!\n",
Packit 400c17
			    name);
Packit 400c17
Packit 400c17
		if (dwarf_attr(die, DW_AT_bit_offset, &attr) == NULL)
Packit 400c17
			fail("Bit offset of member %s missing!\n", name);
Packit 400c17
		(void) dwarf_formudata(&attr, &offset);
Packit 400c17
		if (dwarf_attr(die, DW_AT_bit_size, &attr) == NULL)
Packit 400c17
			fail("Bit size of member %s missing!\n", name);
Packit 400c17
		(void) dwarf_formudata(&attr, &size);
Packit 400c17
Packit 400c17
		obj->is_bitfield = 1;
Packit 400c17
		obj->first_bit = offset;
Packit 400c17
		obj->last_bit = offset + size - 1;
Packit 400c17
	}
Packit 400c17
	die_read_alignment(die, obj);
Packit 400c17
	return obj;
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *print_die_structure(struct cu_ctx *ctx,
Packit 400c17
				  struct record *rec,
Packit 400c17
				  Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	const char *name = get_die_name(die);
Packit 400c17
	unsigned int tag;
Packit 400c17
	obj_list_head_t *members = NULL;
Packit 400c17
	obj_t *obj;
Packit 400c17
	obj_t *member;
Packit 400c17
	Dwarf_Die child_die;
Packit 400c17
Packit 400c17
	obj = obj_struct_new(safe_strdup(name));
Packit 400c17
Packit 400c17
	if (!dwarf_haschildren(die))
Packit 400c17
		goto done;
Packit 400c17
Packit 400c17
	dwarf_child(die, &child_die);
Packit 400c17
	do {
Packit 400c17
		Dwarf_Die *die = &child_die;
Packit 400c17
Packit 400c17
		name = get_die_name(die);
Packit 400c17
		tag = dwarf_tag(die);
Packit 400c17
		if (tag != DW_TAG_member)
Packit 400c17
			fail("Unexpected tag for structure type children: "
Packit 400c17
			    "%s\n", dwarf_tag_string(tag));
Packit 400c17
Packit 400c17
		member = print_die_struct_member(ctx, rec, die, name);
Packit 400c17
		if (members == NULL)
Packit 400c17
			members = obj_list_head_new(member);
Packit 400c17
		else
Packit 400c17
			obj_list_add(members, member);
Packit 400c17
Packit 400c17
	} while (dwarf_siblingof(&child_die, &child_die) == 0);
Packit 400c17
Packit 400c17
	obj->member_list = members;
Packit 400c17
done:
Packit 400c17
	return obj;
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *print_die_enumerator(struct cu_ctx *ctx,
Packit 400c17
				   struct record *rec,
Packit 400c17
				   Dwarf_Die *die,
Packit 400c17
				   const char *name)
Packit 400c17
{
Packit 400c17
	Dwarf_Attribute attr;
Packit 400c17
	Dwarf_Word value;
Packit 400c17
	obj_t *obj;
Packit 400c17
Packit 400c17
	if (dwarf_attr(die, DW_AT_const_value, &attr) == NULL)
Packit 400c17
		fail("Value of enumerator %s missing!\n", name);
Packit 400c17
Packit 400c17
	(void) dwarf_formudata(&attr, &value);
Packit 400c17
Packit 400c17
	obj = obj_constant_new(safe_strdup(name));
Packit 400c17
	obj->constant = value;
Packit 400c17
Packit 400c17
	return obj;
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *print_die_enumeration(struct cu_ctx *ctx,
Packit 400c17
				    struct record *rec,
Packit 400c17
				    Dwarf_Die *die) {
Packit 400c17
	const char *name = get_die_name(die);
Packit 400c17
	obj_list_head_t *members = NULL;
Packit 400c17
	obj_t *member;
Packit 400c17
	obj_t *obj;
Packit 400c17
	Dwarf_Die child_die;
Packit 400c17
Packit 400c17
	obj = obj_enum_new(safe_strdup(name));
Packit 400c17
Packit 400c17
	if (!dwarf_haschildren(die))
Packit 400c17
		goto done;
Packit 400c17
Packit 400c17
	dwarf_child(die, &child_die);
Packit 400c17
	do {
Packit 400c17
		Dwarf_Die *die = &child_die;
Packit 400c17
Packit 400c17
		name = get_die_name(die);
Packit 400c17
		member = print_die_enumerator(ctx, rec, die, name);
Packit 400c17
		if (members == NULL)
Packit 400c17
			members = obj_list_head_new(member);
Packit 400c17
		else
Packit 400c17
			obj_list_add(members, member);
Packit 400c17
	} while (dwarf_siblingof(&child_die, &child_die) == 0);
Packit 400c17
Packit 400c17
	members->object = obj;
Packit 400c17
	obj->member_list = members;
Packit 400c17
done:
Packit 400c17
	return obj;
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *print_die_union(struct cu_ctx *ctx,
Packit 400c17
			      struct record *rec,
Packit 400c17
			      Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	const char *name = get_die_name(die);
Packit 400c17
	unsigned int tag;
Packit 400c17
	obj_list_head_t *members = NULL;
Packit 400c17
	obj_t *member;
Packit 400c17
	obj_t *type;
Packit 400c17
	obj_t *obj;
Packit 400c17
	Dwarf_Die child_die;
Packit 400c17
Packit 400c17
	obj = obj_union_new(safe_strdup(name));
Packit 400c17
Packit 400c17
	if (!dwarf_haschildren(die))
Packit 400c17
		goto done;
Packit 400c17
Packit 400c17
	dwarf_child(die, &child_die);
Packit 400c17
	do {
Packit 400c17
		Dwarf_Die *die = &child_die;
Packit 400c17
Packit 400c17
		name = get_die_name(die);
Packit 400c17
		tag = dwarf_tag(die);
Packit 400c17
		if (tag != DW_TAG_member)
Packit 400c17
			fail("Unexpected tag for union type children: %s\n",
Packit 400c17
			    dwarf_tag_string(tag));
Packit 400c17
Packit 400c17
		type = print_die_type(ctx, rec, die);
Packit 400c17
		member = obj_var_new_add(safe_strdup(name), type);
Packit 400c17
Packit 400c17
		if (members == NULL)
Packit 400c17
			members = obj_list_head_new(member);
Packit 400c17
		else
Packit 400c17
			obj_list_add(members, member);
Packit 400c17
Packit 400c17
	} while (dwarf_siblingof(&child_die, &child_die) == 0);
Packit 400c17
Packit 400c17
	members->object = obj;
Packit 400c17
	obj->member_list = members;
Packit 400c17
done:
Packit 400c17
	die_read_alignment(die, obj);
Packit 400c17
	return obj;
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_list_head_t *print_subprogram_arguments(struct cu_ctx *ctx,
Packit 400c17
						   struct record *rec,
Packit 400c17
						   Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	Dwarf_Die child_die;
Packit 400c17
	obj_t *arg_type;
Packit 400c17
	obj_t *arg;
Packit 400c17
	obj_list_head_t *arg_list = NULL;
Packit 400c17
Packit 400c17
	if (!dwarf_haschildren(die))
Packit 400c17
		return NULL;
Packit 400c17
Packit 400c17
	/* Grab the first argument */
Packit 400c17
	dwarf_child(die, &child_die);
Packit 400c17
Packit 400c17
	/* Walk all arguments until we run into the function body */
Packit 400c17
	while ((dwarf_tag(&child_die) == DW_TAG_formal_parameter) ||
Packit 400c17
	    (dwarf_tag(&child_die) == DW_TAG_unspecified_parameters)) {
Packit 400c17
		const char *name = get_die_name(&child_die);
Packit 400c17
Packit 400c17
		if (dwarf_tag(&child_die) != DW_TAG_unspecified_parameters)
Packit 400c17
			arg_type = print_die_type(ctx, rec, &child_die);
Packit 400c17
		else
Packit 400c17
			arg_type = obj_basetype_new(safe_strdup("..."));
Packit 400c17
Packit 400c17
		arg = obj_var_new_add(safe_strdup(name), arg_type);
Packit 400c17
		if (arg_list == NULL)
Packit 400c17
			arg_list = obj_list_head_new(arg);
Packit 400c17
		else
Packit 400c17
			obj_list_add(arg_list, arg);
Packit 400c17
Packit 400c17
		if (dwarf_siblingof(&child_die, &child_die) != 0)
Packit 400c17
			break;
Packit 400c17
	}
Packit 400c17
	return arg_list;
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *print_die_subprogram(struct cu_ctx *ctx,
Packit 400c17
				   struct record *rec,
Packit 400c17
				   Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	char *name;
Packit 400c17
	obj_list_head_t *arg_list;
Packit 400c17
	obj_t *ret_type;
Packit 400c17
	obj_t *obj;
Packit 400c17
Packit 400c17
	arg_list = print_subprogram_arguments(ctx, rec, die);
Packit 400c17
	ret_type = print_die_type(ctx, rec, die);
Packit 400c17
	name = safe_strdup(get_die_name(die));
Packit 400c17
Packit 400c17
	obj = obj_func_new_add(name, ret_type);
Packit 400c17
	if (arg_list)
Packit 400c17
		arg_list->object = obj;
Packit 400c17
	obj->member_list = arg_list;
Packit 400c17
Packit 400c17
	return obj;
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *_print_die_array_type(struct cu_ctx *ctx,
Packit 400c17
				    struct record *rec,
Packit 400c17
				    Dwarf_Die *child,
Packit 400c17
				    obj_t *base_type)
Packit 400c17
{
Packit 400c17
	Dwarf_Die next_child;
Packit 400c17
	Dwarf_Word value;
Packit 400c17
	Dwarf_Attribute attr;
Packit 400c17
	int rc;
Packit 400c17
	unsigned int tag;
Packit 400c17
	unsigned long arr_idx;
Packit 400c17
	obj_t *obj;
Packit 400c17
	obj_t *sub;
Packit 400c17
Packit 400c17
	if (child == NULL)
Packit 400c17
		return base_type;
Packit 400c17
Packit 400c17
	tag = dwarf_tag(child);
Packit 400c17
	if (tag != DW_TAG_subrange_type)
Packit 400c17
		fail("Unexpected tag for array type children: %s\n",
Packit 400c17
		     dwarf_tag_string(tag));
Packit 400c17
Packit 400c17
	if (dwarf_hasattr(child, DW_AT_upper_bound)) {
Packit 400c17
		(void) dwarf_attr(child, DW_AT_upper_bound, &attr);
Packit 400c17
		(void) dwarf_formudata(&attr, &value);
Packit 400c17
		/* Get the UPPER bound, so add 1 */
Packit 400c17
		arr_idx = value + 1;
Packit 400c17
	} else if (dwarf_hasattr(child, DW_AT_count)) {
Packit 400c17
		(void) dwarf_attr(child, DW_AT_count, &attr);
Packit 400c17
		(void) dwarf_formudata(&attr, &value);
Packit 400c17
		arr_idx = value;
Packit 400c17
	} else {
Packit 400c17
		arr_idx = 0;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	rc = dwarf_siblingof(child, &next_child);
Packit 400c17
	child = rc == 0 ? &next_child : NULL;
Packit 400c17
Packit 400c17
	sub = _print_die_array_type(ctx, rec, child, base_type);
Packit 400c17
	obj = obj_array_new_add(sub);
Packit 400c17
	obj->index = arr_idx;
Packit 400c17
Packit 400c17
	return obj;
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *print_die_array_type(struct cu_ctx *ctx,
Packit 400c17
				   struct record *rec,
Packit 400c17
				   Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	Dwarf_Die child;
Packit 400c17
	obj_t *base_type;
Packit 400c17
Packit 400c17
	/* There should be one child of DW_TAG_subrange_type */
Packit 400c17
	if (!dwarf_haschildren(die))
Packit 400c17
		fail("Array type missing children!\n");
Packit 400c17
Packit 400c17
	base_type = print_die_type(ctx, rec, die);
Packit 400c17
Packit 400c17
	/* Grab the child */
Packit 400c17
	dwarf_child(die, &child);
Packit 400c17
Packit 400c17
	return _print_die_array_type(ctx, rec, &child, base_type);
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *print_die_tag(struct cu_ctx *ctx,
Packit 400c17
			    struct record *rec,
Packit 400c17
			    Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	unsigned int tag = dwarf_tag(die);
Packit 400c17
	const char *name = dwarf_diename(die);
Packit 400c17
	obj_t *obj = NULL;
Packit 400c17
Packit 400c17
	if (tag == DW_TAG_invalid)
Packit 400c17
		fail("DW_TAG_invalid: %s\n", name);
Packit 400c17
Packit 400c17
	switch (tag) {
Packit 400c17
	case DW_TAG_subprogram:
Packit 400c17
		obj = print_die_subprogram(ctx, rec, die);
Packit 400c17
		break;
Packit 400c17
	case DW_TAG_variable:
Packit 400c17
		obj = print_die_type(ctx, rec, die);
Packit 400c17
		obj = obj_var_new_add(safe_strdup(name), obj);
Packit 400c17
		break;
Packit 400c17
	case DW_TAG_base_type:
Packit 400c17
		obj = obj_basetype_new(safe_strdup(name));
Packit 400c17
		break;
Packit 400c17
	case DW_TAG_pointer_type:
Packit 400c17
		obj = print_die_type(ctx, rec, die);
Packit 400c17
		obj = obj_ptr_new_add(obj);
Packit 400c17
		break;
Packit 400c17
	case DW_TAG_structure_type:
Packit 400c17
		obj = print_die_structure(ctx, rec, die);
Packit 400c17
		break;
Packit 400c17
	case DW_TAG_enumeration_type:
Packit 400c17
		obj = print_die_enumeration(ctx, rec, die);
Packit 400c17
		break;
Packit 400c17
	case DW_TAG_union_type:
Packit 400c17
		obj = print_die_union(ctx, rec, die);
Packit 400c17
		break;
Packit 400c17
	case DW_TAG_typedef:
Packit 400c17
		obj = print_die_type(ctx, rec, die);
Packit 400c17
		obj = obj_typedef_new_add(safe_strdup(name), obj);
Packit 400c17
		break;
Packit 400c17
	case DW_TAG_subroutine_type:
Packit 400c17
		obj = print_die_subprogram(ctx, rec, die);
Packit 400c17
		break;
Packit 400c17
	case DW_TAG_volatile_type:
Packit 400c17
		obj = print_die_type(ctx, rec, die);
Packit 400c17
		obj = obj_qualifier_new_add(obj);
Packit 400c17
		obj->base_type = safe_strdup("volatile");
Packit 400c17
		break;
Packit 400c17
	case DW_TAG_const_type:
Packit 400c17
		obj = print_die_type(ctx, rec, die);
Packit 400c17
		obj = obj_qualifier_new_add(obj);
Packit 400c17
		obj->base_type = safe_strdup("const");
Packit 400c17
		break;
Packit 400c17
	case DW_TAG_array_type:
Packit 400c17
		obj = print_die_array_type(ctx, rec, die);
Packit 400c17
		break;
Packit 400c17
	default: {
Packit 400c17
		const char *tagname = dwarf_tag_string(tag);
Packit 400c17
		if (tagname == NULL)
Packit 400c17
			tagname = "<NO TAG>";
Packit 400c17
Packit 400c17
		fail("Unexpected tag for symbol %s: %s\n", name, tagname);
Packit 400c17
		break;
Packit 400c17
	}
Packit 400c17
	}
Packit 400c17
	obj = die_read_alignment(die, obj);
Packit 400c17
	return obj;
Packit 400c17
}
Packit 400c17
Packit 400c17
static obj_t *print_die(struct cu_ctx *ctx,
Packit 400c17
			struct record *parent_file,
Packit 400c17
			Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	char *file;
Packit 400c17
	struct record *rec;
Packit 400c17
	char *old_file;
Packit 400c17
	obj_t *obj;
Packit 400c17
	obj_t *ref_obj;
Packit 400c17
	generate_config_t *conf = ctx->conf;
Packit 400c17
Packit 400c17
	/*
Packit 400c17
	 * Sigh. The type of some fields (eg. struct member as a pointer to
Packit 400c17
	 * another struct) can be defined by a mere declaration without a full
Packit 400c17
	 * specification of the type.  In such cases we just print a remote
Packit 400c17
	 * pointer to the full type and pray it will be printed in a different
Packit 400c17
	 * occasion.
Packit 400c17
	 */
Packit 400c17
Packit 400c17
	/* Check if we need to redirect output or we have a mere declaration */
Packit 400c17
	file = get_symbol_file(die, ctx->cu_die);
Packit 400c17
	if (file == NULL) {
Packit 400c17
		/* no need for new record, output to the current one */
Packit 400c17
		assert(parent_file != NULL);
Packit 400c17
		obj = print_die_tag(ctx, parent_file, die);
Packit 400c17
		return obj;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	ref_obj = obj_reffile_new();
Packit 400c17
Packit 400c17
	/* else handle new record */
Packit 400c17
	rec = record_start(ctx, die, file);
Packit 400c17
	if (rec == NULL)
Packit 400c17
		/* declaration or already processed */
Packit 400c17
		goto out;
Packit 400c17
Packit 400c17
	if (conf->gen_extra)
Packit 400c17
		stack_push(ctx->stack, safe_strdup(file));
Packit 400c17
	obj = print_die_tag(ctx, rec, die);
Packit 400c17
	if (conf->gen_extra)
Packit 400c17
		free(stack_pop(ctx->stack));
Packit 400c17
Packit 400c17
	record_close(rec, obj);
Packit 400c17
Packit 400c17
	old_file = file;
Packit 400c17
	ref_obj->depend_rec_node = list_add(&rec->dependents, ref_obj);
Packit 400c17
	/* if it creates new version, key/file name can change */
Packit 400c17
	file = record_db_add(conf->db, rec);
Packit 400c17
	record_put(rec);
Packit 400c17
	/* record_db_add() returns allocated string */
Packit 400c17
	free(old_file);
Packit 400c17
Packit 400c17
out:
Packit 400c17
	ref_obj->base_type = file;
Packit 400c17
	return ref_obj;
Packit 400c17
}
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * Validate if this is the symbol we should print.
Packit 400c17
 * Returns true if should.
Packit 400c17
 */
Packit 400c17
static bool is_symbol_valid(struct file_ctx *fctx, Dwarf_Die *die)
Packit 400c17
{
Packit 400c17
	const char *name = dwarf_diename(die);
Packit 400c17
	unsigned int tag = dwarf_tag(die);
Packit 400c17
	bool result = false;
Packit 400c17
	generate_config_t *conf = fctx->conf;
Packit 400c17
	struct ksym *ksym1 = NULL;
Packit 400c17
	struct ksym *ksym2;
Packit 400c17
Packit 400c17
	/* Shortcut, unnamed die cannot be part of whitelist */
Packit 400c17
	if (name == NULL)
Packit 400c17
		goto out;
Packit 400c17
Packit 400c17
	/* If symbol file was provided, is the symbol on the list? */
Packit 400c17
	if (conf->symbols != NULL) {
Packit 400c17
		ksym1 = ksymtab_find(conf->symbols, name);
Packit 400c17
		if (ksym1 == NULL)
Packit 400c17
			goto out;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	/* Is this symbol exported in this module with EXPORT_SYMBOL? */
Packit 400c17
	ksym2 = ksymtab_find(fctx->ksymtab, name);
Packit 400c17
	if (ksym2 == NULL)
Packit 400c17
		goto out;
Packit 400c17
Packit 400c17
	/* We don't care about declarations */
Packit 400c17
	if (is_declaration(die))
Packit 400c17
		goto out;
Packit 400c17
	/*
Packit 400c17
	 * Mark the symbol as not eligible to fake symbol generation.
Packit 400c17
	 * We can come till this place with an assembly function,
Packit 400c17
	 * because it may have dwarf info for its C declaration,
Packit 400c17
	 * but others in dwarf are supposed to be normal C functions.
Packit 400c17
	 */
Packit 400c17
	ksymtab_ksym_mark(ksym2);
Packit 400c17
Packit 400c17
	/* Anything EXPORT_SYMBOLed should be external */
Packit 400c17
	if (!is_external(die))
Packit 400c17
		goto out;
Packit 400c17
Packit 400c17
	/* We expect only variables or functions on whitelist */
Packit 400c17
	switch (tag) {
Packit 400c17
	case (DW_TAG_subprogram):
Packit 400c17
		/*
Packit 400c17
		 * We ignore DW_AT_prototyped. This marks functions with
Packit 400c17
		 * arguments specified in their declaration which the old
Packit 400c17
		 * pre-ANSI C didn't require. Unfortunatelly people still omit
Packit 400c17
		 * arguments instead of using foo(void) so we need to handle
Packit 400c17
		 * even functions without DW_AT_prototyped. What a pity!
Packit 400c17
		 */
Packit 400c17
		break;
Packit 400c17
	case DW_TAG_variable:
Packit 400c17
		break;
Packit 400c17
	default:
Packit 400c17
		fail("Symbol %s has unexpected tag: %s!\n", name,
Packit 400c17
		    dwarf_tag_string(tag));
Packit 400c17
	}
Packit 400c17
Packit 400c17
	result = true;
Packit 400c17
Packit 400c17
	/*
Packit 400c17
	 * Mark the symbol as fully processed,
Packit 400c17
	 * so it will not be in the subset of not found symbols.
Packit 400c17
	 * We are talking here about kabi symbols set,
Packit 400c17
	 * which is passed by -s switch.
Packit 400c17
	 *
Packit 400c17
	 * The actual processing starts later in the caller,
Packit 400c17
	 * but the decision is made here.
Packit 400c17
	 */
Packit 400c17
	if (conf->symbols != NULL)
Packit 400c17
		ksymtab_ksym_mark(ksym1);
Packit 400c17
Packit 400c17
out:
Packit 400c17
	return result;
Packit 400c17
}
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * Walk all DIEs in a CU.
Packit 400c17
 * Returns true if the given symbol_name was found, otherwise false.
Packit 400c17
 */
Packit 400c17
static void process_cu_die(Dwarf_Die *cu_die, struct file_ctx *fctx)
Packit 400c17
{
Packit 400c17
	Dwarf_Die child_die;
Packit 400c17
	bool cu_printed = false;
Packit 400c17
	obj_t *ref;
Packit 400c17
	generate_config_t *conf = fctx->conf;
Packit 400c17
Packit 400c17
	if (!dwarf_haschildren(cu_die))
Packit 400c17
		return;
Packit 400c17
Packit 400c17
	/* Walk all DIEs in the CU */
Packit 400c17
	dwarf_child(cu_die, &child_die);
Packit 400c17
	do {
Packit 400c17
		void *data;
Packit 400c17
		struct cu_ctx ctx;
Packit 400c17
Packit 400c17
		if (!is_symbol_valid(fctx, &child_die))
Packit 400c17
			continue;
Packit 400c17
Packit 400c17
		if (!cu_printed && conf->verbose) {
Packit 400c17
			printf("Processing CU %s\n",
Packit 400c17
			       dwarf_diename(cu_die));
Packit 400c17
			cu_printed = true;
Packit 400c17
		}
Packit 400c17
Packit 400c17
		ctx.conf = conf;
Packit 400c17
		ctx.cu_die = cu_die;
Packit 400c17
Packit 400c17
		/* Grab a fresh stack of symbols */
Packit 400c17
		ctx.stack = stack_init();
Packit 400c17
		/* And a set of all processed symbols */
Packit 400c17
		ctx.processed = set_init(PROCESSED_SIZE);
Packit 400c17
Packit 400c17
		/* Print both the CU DIE and symbol DIE */
Packit 400c17
		ref = print_die(&ctx, NULL, &child_die);
Packit 400c17
		obj_free(ref);
Packit 400c17
Packit 400c17
		/* And clear the stack again */
Packit 400c17
		while ((data = stack_pop(ctx.stack)) != NULL)
Packit 400c17
			free(data);
Packit 400c17
Packit 400c17
		stack_destroy(ctx.stack);
Packit 400c17
		set_free(ctx.processed);
Packit 400c17
	} while (dwarf_siblingof(&child_die, &child_die) == 0);
Packit 400c17
}
Packit 400c17
Packit 400c17
static int dwflmod_generate_cb(Dwfl_Module *dwflmod, void **userdata,
Packit 400c17
		const char *name, Dwarf_Addr base, void *arg)
Packit 400c17
{
Packit 400c17
	Dwarf_Addr dwbias;
Packit 400c17
	Dwarf *dbg = dwfl_module_getdwarf(dwflmod, &dwbias);
Packit 400c17
	struct file_ctx *fctx = (struct file_ctx *)arg;
Packit 400c17
Packit 400c17
	if (*userdata != NULL)
Packit 400c17
		fail("Multiple modules found in %s!\n", name);
Packit 400c17
	*userdata = dwflmod;
Packit 400c17
Packit 400c17
	Dwarf_Off off = 0;
Packit 400c17
	Dwarf_Off old_off = 0;
Packit 400c17
	Dwarf_Off type_offset = 0;
Packit 400c17
	Dwarf_Half version;
Packit 400c17
	size_t hsize;
Packit 400c17
	Dwarf_Off abbrev;
Packit 400c17
	uint8_t addresssize;
Packit 400c17
	uint8_t offsetsize;
Packit 400c17
Packit 400c17
	while (dwarf_next_unit(dbg, off, &off, &hsize, &version, &abbrev,
Packit 400c17
	    &addresssize, &offsetsize, NULL, &type_offset) == 0) {
Packit 400c17
		if (version < 2 || version > 4)
Packit 400c17
			fail("Unsupported dwarf version: %d\n", version);
Packit 400c17
Packit 400c17
		/* CU is followed by a single DIE */
Packit 400c17
		Dwarf_Die cu_die;
Packit 400c17
		if (dwarf_offdie(dbg, old_off + hsize, &cu_die) == NULL) {
Packit 400c17
			fail("dwarf_offdie failed for cu!\n");
Packit 400c17
		}
Packit 400c17
Packit 400c17
		process_cu_die(&cu_die, fctx);
Packit 400c17
Packit 400c17
		old_off = off;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	return DWARF_CB_OK;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void generate_type_info(char *filepath, struct file_ctx *ctx)
Packit 400c17
{
Packit 400c17
	static const Dwfl_Callbacks callbacks = {
Packit 400c17
		.section_address = dwfl_offline_section_address,
Packit 400c17
		.find_debuginfo = dwfl_standard_find_debuginfo
Packit 400c17
	};
Packit 400c17
	Dwfl *dwfl = dwfl_begin(&callbacks);
Packit 400c17
Packit 400c17
	if (dwfl_report_offline(dwfl, filepath, filepath, -1) == NULL) {
Packit 400c17
		dwfl_report_end(dwfl, NULL, NULL);
Packit 400c17
		fail("dwfl_report_offline failed: %s\n", dwfl_errmsg(-1));
Packit 400c17
	}
Packit 400c17
	dwfl_report_end(dwfl, NULL, NULL);
Packit 400c17
	dwfl_getmodules(dwfl, &dwflmod_generate_cb, ctx, 0);
Packit 400c17
Packit 400c17
	dwfl_end(dwfl);
Packit 400c17
}
Packit 400c17
Packit 400c17
static bool is_all_done(generate_config_t *conf)
Packit 400c17
{
Packit 400c17
	if (conf->symbols == NULL)
Packit 400c17
		return false;
Packit 400c17
Packit 400c17
	return ksymtab_mark_count(conf->symbols) == conf->symbol_cnt;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void generate_assembly_record(generate_config_t *conf, const char *key)
Packit 400c17
{
Packit 400c17
	struct record *rec;
Packit 400c17
	char *new_key, *name;
Packit 400c17
Packit 400c17
	if (conf->verbose)
Packit 400c17
		printf("Generating assembly record for %s\n", key);
Packit 400c17
Packit 400c17
	safe_asprintf(&name, "asm--%s.txt", key);
Packit 400c17
Packit 400c17
	rec = record_new_assembly(name);
Packit 400c17
	new_key = record_db_add(conf->db, rec);
Packit 400c17
Packit 400c17
	record_put(rec);
Packit 400c17
	free(name);
Packit 400c17
	free(new_key);
Packit 400c17
}
Packit 400c17
Packit 400c17
static bool try_generate_alias(generate_config_t *conf, struct ksym *ksym)
Packit 400c17
{
Packit 400c17
	char *link = ksymtab_ksym_get_link(ksym);
Packit 400c17
	const char *key = ksymtab_ksym_get_name(ksym);
Packit 400c17
	struct record *rec;
Packit 400c17
	char *new_key, *name;
Packit 400c17
Packit 400c17
	if (!link)
Packit 400c17
		return false;
Packit 400c17
Packit 400c17
	if (conf->verbose)
Packit 400c17
		printf("Generating weak record %s -> %s\n",
Packit 400c17
		       key, link);
Packit 400c17
Packit 400c17
	safe_asprintf(&name, "weak--%s.txt", key);
Packit 400c17
Packit 400c17
	rec = record_new_weak(name, link);
Packit 400c17
	new_key = record_db_add(conf->db, rec);
Packit 400c17
Packit 400c17
	record_put(rec);
Packit 400c17
	free(name);
Packit 400c17
	free(new_key);
Packit 400c17
Packit 400c17
	return true;
Packit 400c17
}
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * process_not_found:
Packit 400c17
 *
Packit 400c17
 * Generate fake records for symbols, not found in the debug info,
Packit 400c17
 * but existed in the export list (most probably, assembly function).
Packit 400c17
 *
Packit 400c17
 * If there is a checklist, mark the symbol there (as processed).
Packit 400c17
 * If the symbol not in the checklist, ignore it completely,
Packit 400c17
 * means, do not mark and do not generate fake record for it either.
Packit 400c17
 */
Packit 400c17
static void process_not_found(struct ksym *exported, void *ctx)
Packit 400c17
{
Packit 400c17
	generate_config_t *conf = ctx;
Packit 400c17
	struct ksym *ksym;
Packit 400c17
	const char *key = ksymtab_ksym_get_name(exported);
Packit 400c17
Packit 400c17
	if (ksymtab_ksym_is_marked(exported))
Packit 400c17
		return;
Packit 400c17
Packit 400c17
	if (conf->symbols) {
Packit 400c17
		ksym = ksymtab_find(conf->symbols, key);
Packit 400c17
		if (ksym == NULL)
Packit 400c17
			return;
Packit 400c17
		ksymtab_ksym_mark(ksym);
Packit 400c17
	}
Packit 400c17
Packit 400c17
	if (!try_generate_alias(conf, exported))
Packit 400c17
		generate_assembly_record(conf, key);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void ksymtab_add_alias(struct ksym *ksym, void *ctx)
Packit 400c17
{
Packit 400c17
	struct ksymtab *ksymtab = ctx;
Packit 400c17
Packit 400c17
	ksymtab_copy_sym(ksymtab, ksym);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void ksymtab_add_and_link_alias(struct ksym *ksym, void *ctx)
Packit 400c17
{
Packit 400c17
	struct ksymtab *ksymtab = ctx;
Packit 400c17
	char *link;
Packit 400c17
	const char *name;
Packit 400c17
	struct ksym *link_ksym;
Packit 400c17
Packit 400c17
	link = ksymtab_ksym_get_link(ksym);
Packit 400c17
	link_ksym = ksymtab_find(ksymtab, link);
Packit 400c17
Packit 400c17
	/* if we linked, there must be the symbol in the symtab */
Packit 400c17
	assert(link_ksym != NULL);
Packit 400c17
Packit 400c17
	name = ksymtab_ksym_get_name(ksym);
Packit 400c17
	ksymtab_ksym_set_link(link_ksym, name);
Packit 400c17
Packit 400c17
	ksymtab_copy_sym(ksymtab, ksym);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void merge_aliases(struct ksymtab *ksymtab,
Packit 400c17
			  struct ksymtab *symbols,
Packit 400c17
			  struct ksymtab *aliases)
Packit 400c17
{
Packit 400c17
	ksymtab_for_each(aliases, ksymtab_add_and_link_alias, ksymtab);
Packit 400c17
	if (symbols != NULL)
Packit 400c17
		ksymtab_for_each(aliases, ksymtab_add_alias, symbols);
Packit 400c17
}
Packit 400c17
Packit 400c17
static walk_rv_t process_symbol_file(char *path, void *arg)
Packit 400c17
{
Packit 400c17
	struct file_ctx fctx;
Packit 400c17
	generate_config_t *conf = (generate_config_t *)arg;
Packit 400c17
	struct ksymtab *ksymtab;
Packit 400c17
	struct ksymtab *aliases = NULL;
Packit 400c17
	walk_rv_t ret = WALK_CONT;
Packit 400c17
Packit 400c17
	/* We want to process only .ko kernel modules and vmlinux itself */
Packit 400c17
	if (!safe_strendswith(path, ".ko") &&
Packit 400c17
	    !safe_strendswith(path, "/vmlinux"))
Packit 400c17
		return ret;
Packit 400c17
Packit 400c17
	/*
Packit 400c17
	 * Don't look into RHEL build cache directories.
Packit 400c17
	 */
Packit 400c17
	if (conf->rhel_tree) {
Packit 400c17
		if (strstr(path, "redhat/rpm") != NULL)
Packit 400c17
			return WALK_SKIP;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	ksymtab = ksymtab_read(path, &aliases);
Packit 400c17
Packit 400c17
	if (ksymtab_len(ksymtab) == 0) {
Packit 400c17
		if (conf->verbose)
Packit 400c17
			printf("Skip %s (no exported symbols)\n", path);
Packit 400c17
		goto out;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	merge_aliases(ksymtab, conf->symbols, aliases);
Packit 400c17
Packit 400c17
	fctx.conf = conf;
Packit 400c17
	fctx.ksymtab = ksymtab;
Packit 400c17
Packit 400c17
	if (conf->verbose)
Packit 400c17
		printf("Processing %s\n", path);
Packit 400c17
Packit 400c17
	generate_type_info(path, &fctx);
Packit 400c17
	ksymtab_for_each(ksymtab, process_not_found, conf);
Packit 400c17
Packit 400c17
	if (is_all_done(conf))
Packit 400c17
		ret = WALK_STOP;
Packit 400c17
out:
Packit 400c17
	ksymtab_free(aliases);
Packit 400c17
	ksymtab_free(ksymtab);
Packit 400c17
	return ret;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void print_not_found(struct ksym *ksym, void *ctx)
Packit 400c17
{
Packit 400c17
	const char *s = ksymtab_ksym_get_name(ksym);
Packit 400c17
Packit 400c17
	if (ksymtab_ksym_is_marked(ksym))
Packit 400c17
		return;
Packit 400c17
	printf("%s not found!\n", s);
Packit 400c17
}
Packit 400c17
Packit 400c17
struct record *record_copy(struct record *src)
Packit 400c17
{
Packit 400c17
	struct record *res = record_new_regular("");
Packit 400c17
	obj_t *o1 = record_obj(src);
Packit 400c17
Packit 400c17
	res->obj = obj_merge(o1, o1, MERGE_DECL);
Packit 400c17
	obj_fill_parent(res->obj);
Packit 400c17
	res->origin = safe_strdup(src->origin);
Packit 400c17
	res->base_file = NULL;
Packit 400c17
Packit 400c17
	return res;
Packit 400c17
}
Packit 400c17
Packit 400c17
bool record_list_can_merge(struct list *rec_list)
Packit 400c17
{
Packit 400c17
	bool result = true;
Packit 400c17
	struct record *merger;
Packit 400c17
	struct list_node *iter;
Packit 400c17
Packit 400c17
	if (list_len(rec_list) <= 1) {
Packit 400c17
		/* only one record -> nothing to merge */
Packit 400c17
		return false;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	merger = record_copy(list_node_data(rec_list->first));
Packit 400c17
	LIST_FOR_EACH(rec_list, iter) {
Packit 400c17
		struct record *record = list_node_data(iter);
Packit 400c17
Packit 400c17
		if (!record_merge(merger, record, MERGE_DECL)) {
Packit 400c17
			result = false;
Packit 400c17
			break;
Packit 400c17
		}
Packit 400c17
	}
Packit 400c17
	record_put(merger);
Packit 400c17
Packit 400c17
	return result;
Packit 400c17
}
Packit 400c17
Packit 400c17
void record_list_merge(struct list *rec_list)
Packit 400c17
{
Packit 400c17
	struct record *first = rec_list->first->data;
Packit 400c17
	struct list_node *next = rec_list->first->next;
Packit 400c17
	struct list_node *curr;
Packit 400c17
Packit 400c17
	struct record *record;
Packit 400c17
Packit 400c17
	while (next != NULL) {
Packit 400c17
		curr = next;
Packit 400c17
		next = next->next;
Packit 400c17
Packit 400c17
		record = curr->data;
Packit 400c17
Packit 400c17
		list_concat(&first->dependents, &record->dependents);
Packit 400c17
		record_merge(first, record, MERGE_DECL);
Packit 400c17
		record_put(record);
Packit 400c17
Packit 400c17
		free(curr);
Packit 400c17
	}
Packit 400c17
Packit 400c17
	record_update_dependents(first);
Packit 400c17
	rec_list->first->next = NULL;
Packit 400c17
	rec_list->last = rec_list->first;
Packit 400c17
	rec_list->len = 1;
Packit 400c17
}
Packit 400c17
Packit 400c17
void record_db_merge(struct record_db *db)
Packit 400c17
{
Packit 400c17
	struct hash *hash = (struct hash *)db;
Packit 400c17
	bool merged;
Packit 400c17
	struct hash_iter iter;
Packit 400c17
	const char *key;
Packit 400c17
	const void *val;
Packit 400c17
Packit 400c17
	do {
Packit 400c17
		merged = false;
Packit 400c17
Packit 400c17
		hash_iter_init(hash, &iter);
Packit 400c17
		while (hash_iter_next(&iter, &key, &val)) {
Packit 400c17
			struct list *list = (struct list *)val;
Packit 400c17
Packit 400c17
			if (list != NULL && record_list_can_merge(list)) {
Packit 400c17
				record_list_merge(list);
Packit 400c17
				merged = true;
Packit 400c17
			}
Packit 400c17
		}
Packit 400c17
	} while (merged);
Packit 400c17
}
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * Print symbol definition by walking all DIEs in a .debug_info section.
Packit 400c17
 * Returns true if the definition was printed, otherwise false.
Packit 400c17
 */
Packit 400c17
static void generate_symbol_defs(generate_config_t *conf)
Packit 400c17
{
Packit 400c17
	struct stat st;
Packit 400c17
Packit 400c17
	if (stat(conf->kernel_dir, &st) != 0)
Packit 400c17
		fail("Failed to stat %s: %s\n", conf->kernel_dir,
Packit 400c17
		    strerror(errno));
Packit 400c17
Packit 400c17
	/* Lets walk the normal modules */
Packit 400c17
	printf("Generating symbol defs from %s...\n", conf->kernel_dir);
Packit 400c17
Packit 400c17
	conf->db = record_db_init();
Packit 400c17
Packit 400c17
	if (S_ISDIR(st.st_mode)) {
Packit 400c17
		walk_dir(conf->kernel_dir, false, process_symbol_file, conf);
Packit 400c17
	} else if (S_ISREG(st.st_mode)) {
Packit 400c17
		char *path = conf->kernel_dir;
Packit 400c17
		conf->kernel_dir = "";
Packit 400c17
		process_symbol_file(path, conf);
Packit 400c17
	} else {
Packit 400c17
		fail("Not a file or directory: %s\n", conf->kernel_dir);
Packit 400c17
	}
Packit 400c17
Packit 400c17
	ksymtab_for_each(conf->symbols, print_not_found, NULL);
Packit 400c17
Packit 400c17
	record_db_merge(conf->db);
Packit 400c17
Packit 400c17
	record_db_dump(conf->db, conf->kabi_dir);
Packit 400c17
	record_db_free(conf->db);
Packit 400c17
}
Packit 400c17
Packit 400c17
#define	WHITESPACE	" \t\n"
Packit 400c17
Packit 400c17
/* Remove white characters from given buffer */
Packit 400c17
static void strip(char *buf)
Packit 400c17
{
Packit 400c17
	size_t i = 0, j = 0;
Packit 400c17
	while (buf[j] != '\0') {
Packit 400c17
		if (strchr(WHITESPACE, buf[j]) == NULL) {
Packit 400c17
			if (i != j)
Packit 400c17
				buf[i] = buf[j];
Packit 400c17
			i++;
Packit 400c17
		}
Packit 400c17
		j++;
Packit 400c17
	}
Packit 400c17
	buf[i] = '\0';
Packit 400c17
}
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * Check if the string is valid C identifier.
Packit 400c17
 * We do this so we can easily provide the standard kabi whitelist file as the
Packit 400c17
 * symbol list.
Packit 400c17
 */
Packit 400c17
static bool is_valid_c_identifier(char *s)
Packit 400c17
{
Packit 400c17
	int i, len;
Packit 400c17
Packit 400c17
	if (s == NULL)
Packit 400c17
		return false;
Packit 400c17
Packit 400c17
	len = strlen(s);
Packit 400c17
	if (len == 0)
Packit 400c17
		return false;
Packit 400c17
	if (s[0] != '_' && !isalpha(s[0]))
Packit 400c17
		return false;
Packit 400c17
Packit 400c17
	for (i = 1; i < len; i++) {
Packit 400c17
		if (s[i] != '_' && !isalnum(s[i]))
Packit 400c17
			return false;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	return true;
Packit 400c17
}
Packit 400c17
Packit 400c17
static bool is_kabi_header(char *s)
Packit 400c17
{
Packit 400c17
	const char *suffix = "_whitelist]";
Packit 400c17
	int suffixlen = strlen(suffix);
Packit 400c17
	int len;
Packit 400c17
Packit 400c17
	assert(s != NULL);
Packit 400c17
Packit 400c17
	len = strlen(s);
Packit 400c17
	if (len <= suffixlen + 1)
Packit 400c17
		return false;
Packit 400c17
Packit 400c17
	if (s[0] != '[')
Packit 400c17
		return false;
Packit 400c17
Packit 400c17
	if (strcmp(s + (len - suffixlen), suffix) != 0)
Packit 400c17
		return false;
Packit 400c17
Packit 400c17
	return true;
Packit 400c17
}
Packit 400c17
Packit 400c17
/* Get list of symbols to generate. */
Packit 400c17
static struct ksymtab *read_symbols(char *filename)
Packit 400c17
{
Packit 400c17
	FILE *fp = fopen(filename, "r");
Packit 400c17
	char *line = NULL;
Packit 400c17
	size_t len = 0;
Packit 400c17
	size_t i = 0;
Packit 400c17
	struct ksymtab *symbols;
Packit 400c17
Packit 400c17
	symbols = ksymtab_new(DEFAULT_BUFSIZE);
Packit 400c17
Packit 400c17
	if (fp == NULL)
Packit 400c17
		fail("Failed to open symbol file: %s\n", strerror(errno));
Packit 400c17
Packit 400c17
	errno = 0;
Packit 400c17
	while ((getline(&line, &len, fp)) != -1) {
Packit 400c17
		strip(line);
Packit 400c17
		if (!is_valid_c_identifier(line)) {
Packit 400c17
			if (!is_kabi_header(line)) {
Packit 400c17
				printf("WARNING: Ignoring line \'%s\' from the"
Packit 400c17
				    " symbols file as it's not a valid C "
Packit 400c17
				    "identifier.\n", line);
Packit 400c17
			}
Packit 400c17
			continue;
Packit 400c17
		}
Packit 400c17
		ksymtab_add_sym(symbols, line, len, i);
Packit 400c17
		i++;
Packit 400c17
	}
Packit 400c17
Packit 400c17
	if (errno != 0)
Packit 400c17
		fail("getline() failed for %s: %s\n", filename,
Packit 400c17
		    strerror(errno));
Packit 400c17
Packit 400c17
	if (line != NULL)
Packit 400c17
		free(line);
Packit 400c17
Packit 400c17
	fclose(fp);
Packit 400c17
Packit 400c17
	return symbols;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void generate_usage()
Packit 400c17
{
Packit 400c17
	printf("Usage:\n"
Packit 400c17
	       "\tgenerate [options] kernel_dir\n"
Packit 400c17
	       "\nOptions:\n"
Packit 400c17
	       "    -h, --help:\t\tshow this message\n"
Packit 400c17
	       "    -v, --verbose:\tdisplay debug information\n"
Packit 400c17
	       "    -o, --output kabi_dir:\n\t\t\t"
Packit 400c17
	       "where to write kabi files (default: \"output\")\n"
Packit 400c17
	       "    -s, --symbols symbol_file:\n\t\t\ta file containing the"
Packit 400c17
	       " list of symbols of interest (e.g. whitelisted)\n"
Packit 400c17
	       "    -r, --rhel:\n\t\t\trun on the RHEL build tree\n"
Packit 400c17
	       "    -a, --abs-path abs_path:\n\t\t\t"
Packit 400c17
	       "replace the absolute path by a relative path\n"
Packit 400c17
	       "    -g, --generate-extra-info:\n\t\t\t"
Packit 400c17
	       "generate extra information (declaration stack, compilation unit)\n");
Packit 400c17
	exit(1);
Packit 400c17
}
Packit 400c17
Packit 400c17
static void parse_generate_opts(int argc, char **argv, generate_config_t *conf,
Packit 400c17
		char **symbol_file)
Packit 400c17
{
Packit 400c17
	*symbol_file = NULL;
Packit 400c17
	conf->rhel_tree = false;
Packit 400c17
	conf->verbose = false;
Packit 400c17
	conf->kabi_dir = DEFAULT_OUTPUT_DIR;
Packit 400c17
	int opt, opt_index;
Packit 400c17
	struct option loptions[] = {
Packit 400c17
		{"help", no_argument, 0, 'h'},
Packit 400c17
		{"verbose", no_argument, 0, 'v'},
Packit 400c17
		{"output", required_argument, 0, 'o'},
Packit 400c17
		{"symbols", required_argument, 0, 's'},
Packit 400c17
		{"rhel", no_argument, 0, 'r'},
Packit 400c17
		{"abs-path", required_argument, 0, 'a'},
Packit 400c17
		{"generate-extra-info", no_argument, 0, 'g'},
Packit 400c17
		{0, 0, 0, 0}
Packit 400c17
	};
Packit 400c17
Packit 400c17
	while ((opt = getopt_long(argc, argv, "hvo:s:ra:m:g",
Packit 400c17
				  loptions, &opt_index)) != -1) {
Packit 400c17
		switch (opt) {
Packit 400c17
		case 'h':
Packit 400c17
			generate_usage();
Packit 400c17
		case 'v':
Packit 400c17
			conf->verbose = true;
Packit 400c17
			break;
Packit 400c17
		case 'o':
Packit 400c17
			conf->kabi_dir = optarg;
Packit 400c17
			break;
Packit 400c17
		case 's':
Packit 400c17
			*symbol_file = optarg;
Packit 400c17
			break;
Packit 400c17
		case 'r':
Packit 400c17
			conf->rhel_tree = true;
Packit 400c17
			break;
Packit 400c17
		case 'a':
Packit 400c17
			get_file_replace_path = optarg;
Packit 400c17
			break;
Packit 400c17
		case 'g':
Packit 400c17
			conf->gen_extra = true;
Packit 400c17
			break;
Packit 400c17
		default:
Packit 400c17
			generate_usage();
Packit 400c17
		}
Packit 400c17
	}
Packit 400c17
Packit 400c17
	if (optind != argc - 1)
Packit 400c17
		generate_usage();
Packit 400c17
Packit 400c17
	conf->kernel_dir = argv[optind];
Packit 400c17
Packit 400c17
	rec_mkdir(conf->kabi_dir);
Packit 400c17
}
Packit 400c17
Packit 400c17
void generate(int argc, char **argv)
Packit 400c17
{
Packit 400c17
	char *symbol_file;
Packit 400c17
	generate_config_t *conf = safe_zmalloc(sizeof(*conf));
Packit 400c17
Packit 400c17
	parse_generate_opts(argc, argv, conf, &symbol_file);
Packit 400c17
Packit 400c17
	if (symbol_file != NULL) {
Packit 400c17
		conf->symbols = read_symbols(symbol_file);
Packit 400c17
		conf->symbol_cnt = ksymtab_len(conf->symbols);
Packit 400c17
Packit 400c17
		if (conf->verbose)
Packit 400c17
			printf("Loaded %ld symbols\n", conf->symbol_cnt);
Packit 400c17
	}
Packit 400c17
Packit 400c17
	generate_symbol_defs(conf);
Packit 400c17
Packit 400c17
	if (symbol_file != NULL)
Packit 400c17
		ksymtab_free(conf->symbols);
Packit 400c17
Packit 400c17
	free(conf);
Packit 400c17
}